Sorry, something went wrong.
|
Idea: Create Axes on a graphic, useful when the graphic has an offset. Example use cases: line stack, many images in one subplot. For LineStack especially it can be useful to have independent y-rulers for each, and just one x-ruler. Can be Graphic.add_axes() Maybe we should make Axes a Graphic so then it's easy to link the offset and rotation. The start and end position of the rulers for this should be the box of the graphic. |
Sorry, something went wrong.
|
Very nice to see this taking shape! Looks good so far to me :) |
Sorry, something went wrong.
|
Let's try to merge a prototype of this towards the end of this week/early next week and we can evolve it over time with use. It is messy but we can refactor :D Updates: Can add axes to a single graphic, useful for line stacks or any graphics that are offset from the origin, example: line_collection.add_axes() caveat: very slow if there are lots of graphics with these I did not make Axes a graphic, it doesn't require all that functionality and we avoid circular imports. I think let's keep it this way. For perspective projections, it was non-trivial to map the screen space to world space in all 3 dimensions, so instead what it will do for perspective projections is just get the scene bbox and set the axes limits based on the scene bbox 😄 . I think this is a good enough compromise for now. |
Sorry, something went wrong.
|
I just realized, if axes are added to a graphic directly, it's not necessary to update them on every render cycle. They can just be fixed. Change only if the graphic offset and rotation change. |
Sorry, something went wrong.
|
@clewis7 Once you've gotten further in pynaviz the Axes tick label mapping functions will be really important for you. For example, let's say you have a HeatmapVisual to display subsampled data of a massive recording. You could have some property like HeatmapVisual.graphic_data_subsample = (1, 1_000): subsample by (1, 1_000) in [rows, columns], and columns is typically time which is usually the subsampled dimension. When you subscribe a HeatmapVisual causes the time to change, the TimeStore sees the graphic data subsample factor and adjusts accordingly to get the real time. This real time is then used for any other mappings. |
Sorry, something went wrong.
|
This looks amazing!! So cool :D |
Sorry, something went wrong.
|
I subclassed pygfx.Ruler and added two attributes:
I also set the TextMaterial antialiasing to False because the text labels seems easier to read when they're more crisp since they're so small 🤔 Let's kinda keep both of these hidden for now, let's play with it ourselves and you can play with it in pynapple and we can iterate a more final API later. @apasarkar this tick mapping stuff is useful for you too! to show actual time units instead of just frame index as the x ticks Example of how tick_text_mapper can be used: import fastplotlib as fpl
import numpy as np
a = np.random.rand(1_000)
fig = fpl.Figure()
line = fig[0, 0].add_line(a)
def basic_mapper(text: str):
"""map values by dividing by 30"""
value = float(text)
# divide by 30, show in seconds
return f"{value / 30:.2f}s"
indexer = np.linspace(0, 20, 1_000)
def array_mapper(text: str):
"""map values by indexing an array, ex. time axis from pynapple arrays"""
value = int(text)
if value >= 0 and value < indexer.size:
return f"{indexer[value]:.2f}s"
return "" # no match, return nothing
fig[0, 0].axes.x.tick_text_mapper = basic_mapper
fig.show(maintain_aspect=False)
fpl.run()
Basically the tick_text_mapper func can be anything that takes a tick str label and returns a str label. using basic_mapper: using array_mapper: A few caveatsAxes rendering is weird in docs galleryBut they render properly when run 🤷♂️ . In Figure.show() it will set the axes using the scene bbox when using a WgpuManualOffscreenCanvas such as during tests and the examples gallery. issue 1, the bbox should encapsulate the entire scene issue 2, no axes in the iris scatter plots, probably zoomed out of far clipping plane (too zoomed in) Test screenshotsdoes not render the grid through the entire canvas |
Sorry, something went wrong.
|
I think it might actually be taking forever at the scatter_size test example which is really bizarre ok I set max concurrent runners to 4 instead of 8, let's see 🤷♂️ I also set a max timeout of 10 mins for any CI actions on the bigmem runner |
Sorry, something went wrong.
|
I removed selectors from testutils for now, let's see |
Sorry, something went wrong.
|
ok I think the issue actually is when it goes from test_examples_run to test_examples_screenshots, I guess something about how we now have hte render call in Figure.show() for the manual offscreen canvas is causing weird things. But only for tests and not for docs gallery, and only on github actions and not local. |
Sorry, something went wrong.
|
Seems like the viewport.render() call is blocking CI but only on github actions and not locally even with lavapipe |
Sorry, something went wrong.
|
ok tests should pass now, the issue is something in the render call is blocking test_examples So I modified Figure.show() for offscreen canvas to this: elif self.canvas.__class__.__name__ == "WgpuManualOffscreenCanvas":
# for test and docs gallery screenshots
for subplot in self:
subplot.set_viewport_rect()
subplot.axes.update_using_camera()
# render call is blocking only on github actions for some reason,
# but not for rtd build, this is a workaround
# for CI tests, the render call works if it's in test_examples
# but it is necessary for the gallery images too so that's why this check is here
if "RTD_BUILD" in os.environ.keys():
if os.environ["RTD_BUILD"] == "1":
subplot.viewport.render(subplot.scene, subplot.camera)
And in test_example_screenshots perform the render & flush # import the example module
example = importlib.import_module(module_name)
for subplot in example.figure:
subplot.viewport.render(subplot.scene, subplot.camera)
example.figure.renderer.flush()
# render a frame
img = np.asarray(example.figure.renderer.target.draw())
|
Sorry, something went wrong.
Started prototyping axes. A lot of this is based on the pygfx ruler example. @almarklein would be great to get your input on this, this is just a prototype so I'm more than happy if you had other ideas on how to do this! I was too excited to not protype this 😄
also closes #15
Summary:
Grid class
subclass pygfx.Grid to give it properties from the GridMaterial so the user can for example do subplot.axes.grids.xy.major_step instead of subplot.axes.grids.xy.material.major_step
Grids
Just a pygfx.Group with xy, xz, yz attributes for each Grid to make it easier to manage all grids together, for example:
hide all grids:
subplot.axes.grids.visible = False
Axes class
The main properties are:
Has auto_update animation function that runs on each render cycle to update the Axes based on the current camera state.
Subplots auto-make an Axes instance. The auto_update function to update the axes are added to the subplot's render functions list.
I also had to slightly modify PlotArea. There's a new pygfx.Group: PlotArea._graphics_scene which sequesters all the WorldObjects that belong to Graphics in the PlotArea. This allows us to compute the world bounding box on the actual plot-data-graphics and exclude the axes, selectors, etc.
PlotArea._graphics_scene is added to PlotArea.scene, and non-Graphic WorldObjects, such as legends, the axes, and selector tools, are added directly to PlotArea.scene.
Todo:
- Not working with camera.fov > 0, need to figure out why.
- z axis ruler
- figure out how to set PlotArea.auto_scale() such that the rulers are in the bottom left corner, basically take into account the ruler spacing from the subplot edge so that all the Graphics are "contained" within the rulers when auto_scale() is used, right now it's wonky with the current auto_scale. I think just reducing the zoom should be sufficient?
- subclass pygfx.Ruler to add:
- setting font size
- mapping function to map the tick labels in world space + offset to the user's desired output space, for example time units
- modify all test screenshots again 🙃
axes1-2024-07-11_00.24.26.mp4 axes2-2024-07-11_00.25.58.mp4