Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Merged
Show file tree
Hide file tree
Changes from 3 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
52 changes: 52 additions & 0 deletions 52 examples/gridplot_simple.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import numpy as np
from wgpu.gui.auto import WgpuCanvas
import pygfx as gfx
from fastplotlib import GridPlot, Line, Scatter, Histogram
from fastplotlib import Image, GridPlot, run
from math import sin, cos, radians

# GridPlot of shape 2 x 3
grid_plot = GridPlot(shape=(2, 3))

image_graphics = list()

hist_data1 = np.random.normal(0, 256, 2048)
hist_data2 = np.random.poisson(0, 256)

# Make a random image graphic for each subplot
for i, subplot in enumerate(grid_plot):
img = np.random.rand(512, 512) * 255
ig = Image(data=img, vmin=0, vmax=255, cmap='gnuplot2')
image_graphics.append(ig)

# add the graphic to the subplot
subplot.add_graphic(ig)

histogram = Histogram(data=hist_data1, bins=100)
histogram.world_object.rotation.w = cos(radians(45))
histogram.world_object.rotation.z = sin(radians(45))

histogram.world_object.scale.y = 1
histogram.world_object.scale.x = 8

for dv_position in ["right", "top", "bottom", "left"]:
h2 = Histogram(data=hist_data1, bins=100)

subplot.docked_viewports[dv_position].size = 60
subplot.docked_viewports[dv_position].add_graphic(h2)
#

# Define a function to update the image graphics
# with new randomly generated data
def set_random_frame():
for ig in image_graphics:
new_data = np.random.rand(512, 512) * 255
ig.update_data(data=new_data)


# add the animation
grid_plot.add_animations([set_random_frame])

grid_plot.show()

run()
1 change: 1 addition & 0 deletions 1 fastplotlib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from .subplot import Subplot
from .plot import Plot
from pathlib import Path
from wgpu.gui.auto import run


with open(Path(__file__).parent.joinpath("VERSION"), "r") as f:
Expand Down
196 changes: 191 additions & 5 deletions 196 fastplotlib/subplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from typing import *
from wgpu.gui.auto import WgpuCanvas
from warnings import warn
import numpy as np


class Subplot:
Expand Down Expand Up @@ -59,21 +60,34 @@ def __init__(

self._animate_funcs = list()

self.renderer.add_event_handler(self._produce_rect, "resize")
self.renderer.add_event_handler(self.set_viewport_rect, "resize")

def _produce_rect(self, *args):#, w, h):
i, j = self.position
self.docked_viewports = dict()
for pos in ["left", "top", "right", "bottom"]:
self.docked_viewports[pos] = _DockedViewport(self, pos, size=0)

self.set_viewport_rect()

def get_rect(self):
i, j = self.position
w, h = self.renderer.logical_size

spacing = 2 # spacing in pixels

self.viewport.rect = [
return np.array([
Comment thread
kushalkolar marked this conversation as resolved.
Outdated
((w / self.ncols) + ((j - 1) * (w / self.ncols))) + spacing,
((h / self.nrows) + ((i - 1) * (h / self.nrows))) + spacing,
(w / self.ncols) - spacing,
(h / self.nrows) - spacing
]
])

def set_viewport_rect(self, *args):
rect = self.get_rect()

for dv in self.docked_viewports.values():
rect = rect + dv.get_parent_rect_adjust()

self.viewport.rect = rect

def animate(self, canvas_dims: Tuple[int, int] = None):
self.controller.update_camera(self.camera)
Expand All @@ -82,6 +96,9 @@ def animate(self, canvas_dims: Tuple[int, int] = None):
for f in self._animate_funcs:
f()

for dv in self.docked_viewports.values():
dv.render()

def add_animations(self, funcs: List[callable]):
self._animate_funcs += funcs

Expand Down Expand Up @@ -140,3 +157,172 @@ def set_grid_visibility(self, visible: bool):

def remove_graphic(self, graphic):
self.scene.remove(graphic.world_object)


class _DockedViewport:
_valid_positions = [
"right",
"left",
"top",
"bottom"
]

def __init__(
self,
parent: Subplot,
position: str,
size: int,
camera: str = "2d",
):
if position not in self._valid_positions:
raise ValueError(f"the `position` of an AnchoredViewport must be one of: {self._valid_positions}")

self.parent = parent
self.position = position
self._size = size

self.viewport = pygfx.Viewport(self.parent.renderer)

self.scene = pygfx.Scene()
self.scene.add(
pygfx.Background(None, pygfx.BackgroundMaterial((0.2, 0.0, 0, 1), (0, 0.0, 0.2, 1)))
)

self.camera = pygfx.OrthographicCamera()
self.controller = pygfx.PanZoomController()

self.controller.add_default_event_handlers(
self.viewport,
self.camera
)

self.parent.renderer.add_event_handler(self.set_viewport_rect, "resize")
self.set_viewport_rect()

@property
def size(self) -> int:
return self._size

@size.setter
def size(self, s: int):
self._size = s
self.parent.set_viewport_rect()
self.set_viewport_rect()

def _get_rect(self, *args):
if self.size == 0:
self.viewport.rect = None
return

i, j = self.parent.position
w, h = self.parent.renderer.logical_size

spacing = 2 # spacing in pixels

if self.position == "right":
r = [
(w / self.parent.ncols) + ((j - 1) * (w / self.parent.ncols)) + (w / self.parent.ncols) - self.size,
((h / self.parent.nrows) + ((i - 1) * (h / self.parent.nrows))) + spacing,
self.size,
(h / self.parent.nrows) - spacing
]

elif self.position == "left":
r = [
(w / self.parent.ncols) + ((j - 1) * (w / self.parent.ncols)),
((h / self.parent.nrows) + ((i - 1) * (h / self.parent.nrows))) + spacing,
self.size,
(h / self.parent.nrows) - spacing
]

elif self.position == "top":
r = [
(w / self.parent.ncols) + ((j - 1) * (w / self.parent.ncols)) + spacing,
((h / self.parent.nrows) + ((i - 1) * (h / self.parent.nrows))) + spacing,
(w / self.parent.ncols) - spacing,
self.size
]

elif self.position == "bottom":
r = [
(w / self.parent.ncols) + ((j - 1) * (w / self.parent.ncols)) + spacing,
((h / self.parent.nrows) + ((i - 1) * (h / self.parent.nrows))) + (h / self.parent.nrows) - self.size,
(w / self.parent.ncols) - spacing,
self.size
]
else:
raise ValueError("invalid position")

return r

def set_viewport_rect(self, *args):
rect = self._get_rect()

self.viewport.rect = rect

def get_parent_rect_adjust(self):
if self.position == "right":
return np.array([
0, # parent subplot x-position is same
0,
-self.size, # width of parent subplot is `self.size` smaller
0
])

elif self.position == "left":
return np.array([
self.size, # `self.size` added to parent subplot x-position
0,
-self.size, # width of parent subplot is `self.size` smaller
0
])

elif self.position == "top":
return np.array([
0,
self.size, # `self.size` added to parent subplot y-position
0,
-self.size, # height of parent subplot is `self.size` smaller
])

elif self.position == "bottom":
return np.array([
0,
0, # parent subplot y-position is same,
0,
-self.size, # height of parent subplot is `self.size` smaller
])

def render(self):
if self.size == 0:
return

self.controller.update_camera(self.camera)
self.viewport.render(self.scene, self.camera)

def add_graphic(self, graphic, center: bool = True):
self.scene.add(graphic.world_object)

if center:
self.center_graphic(graphic)

def _refresh_camera(self):
self.controller.update_camera(self.camera)
# if sum(self.renderer.logical_size) > 0:
# scene_lsize = self.viewport.rect[2], self.viewport.rect[3]
# else:
# scene_lsize = (1, 1)
#
# self.camera.set_view_size(*scene_lsize)
self.camera.update_projection_matrix()

def center_graphic(self, graphic, zoom: float = 1.3):
if not isinstance(self.camera, pygfx.OrthographicCamera):
warn("`center_graphic()` not yet implemented for `PerspectiveCamera`")
return

self._refresh_camera()

self.controller.show_object(self.camera, graphic.world_object)

self.controller.zoom(zoom)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.