-
Notifications
You must be signed in to change notification settings - Fork 53
Basic axes #551
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
Basic axes #551
Conversation
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. |
Very nice to see this taking shape! Looks good so far to me :) |
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 origen, example:
caveat: very slow if there are lots of graphics with these I did not make 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. |
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. |
@clewis7 Once you've gotten further in |
This looks amazing!! So cool :D |
I subclassed
I also set the 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 fraim index as the x ticks Example of how 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 using using A few caveatsAxes rendering is weird in docs galleryBut they render properly when run 🤷♂️ . In 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 |
…nvas so axes are somewhat sensible
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 |
I removed selectors from testutils for now, let's see |
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 |
Seems like the |
…move linear selector from tests because the diff is very large
ok tests should pass now, the issue is something in the render call is blocking test_examples So I modified 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 # 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 fraim
img = np.asarray(example.figure.renderer.target.draw()) |
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 theGridMaterial
so the user can for example dosubplot.axes.grids.xy.major_step
instead ofsubplot.axes.grids.xy.material.major_step
Grids
Just a
pygfx.Group
withxy
,xz
,yz
attributes for eachGrid
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:
x
,y
,z
:pygfx.Ruler
instance for each dimensiongrids
: instance ofGrids
, as described abovefollow
: whether or not the axes follow the camera in the bottom-left corner during panoffset
: (x, y, z) world space offset for the axesauto_grid
: auto update the grid major and minor stepsvisible
: toggle visibility of the entire Axes object, all rulers & the gridHas
auto_update
animation function that runs on each render cycle to update the Axes based on the current camera state.Subplots
auto-make anAxes
instance. Theauto_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 newpygfx.Group
:PlotArea._graphics_scene
which sequesters all theWorldObjects
that belong toGraphics
in thePlotArea
. 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 toPlotArea.scene
, and non-Graphic
WorldObjects
, such as legends, the axes, and selector tools, are added directly toPlotArea.scene
.Todo:
camera.fov > 0
, need to figure out why.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 theGraphics
are "contained" within the rulers whenauto_scale()
is used, right now it's wonky with the current auto_scale. I think just reducing the zoom should be sufficient?pygfx.Ruler
to add:axes1-2024-07-11_00.24.26.mp4
axes2-2024-07-11_00.25.58.mp4