GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Basic axes by kushalkolar · Pull Request #551 · fastplotlib/fastplotlib

Conversation

@kushalkolar

@kushalkolar kushalkolar commented Jul 11, 2024 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

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:

  • x, y, z: pygfx.Ruler instance for each dimension
  • grids: instance of Grids, as described above
  • follow: whether or not the axes follow the camera in the bottom-left corner during pan
  • offset: (x, y, z) world space offset for the axes
  • auto_grid: auto update the grid major and minor steps
  • visible: toggle visibility of the entire Axes object, all rulers & the grid

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

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

almarklein reacted with heart emoji
All reactions
  • ❤️ 1 reaction
kushalkolar added 4 commits July 10, 2024 03:02
@kushalkolar
add colorbar to hlut tool
142e67d
@kushalkolar
pause_events contextmanager
67b2b75
@kushalkolar
cleanup
323c57c
@kushalkolar
very basic stuff works
7c5e294
@kushalkolar

kushalkolar commented Jul 12, 2024

Copy link
Copy Markdown
Member Author

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.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@almarklein

almarklein commented Jul 15, 2024

Copy link
Copy Markdown
Collaborator

Very nice to see this taking shape! Looks good so far to me :)

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@kushalkolar
perspective projection implementation
6e05129
@kushalkolar

kushalkolar commented Jul 18, 2024 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member Author

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()

image

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.

image

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

kushalkolar added 3 commits July 18, 2024 03:05
@kushalkolar
Graphic.add_axes, refactor grids into an object instead of dict
0717560
@kushalkolar
update example
e94fedb
@kushalkolar
docstrings
403b70c
kushalkolar
kushalkolar commented Jul 18, 2024
View reviewed changes
Comment thread fastplotlib/layouts/_figure.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

kushalkolar
kushalkolar commented Jul 18, 2024
View reviewed changes
Comment thread fastplotlib/graphics/_axes.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

@kushalkolar

kushalkolar commented Jul 18, 2024

Copy link
Copy Markdown
Member Author

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.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@kushalkolar

kushalkolar commented Jul 19, 2024

Copy link
Copy Markdown
Member Author

@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.

clewis7 reacted with rocket emoji
All reactions
  • 🚀 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@clewis7

clewis7 commented Jul 19, 2024

Copy link
Copy Markdown
Member

This looks amazing!! So cool :D

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@kushalkolar
Merge branch 'hlut-cbar' into axes
2118477
@kushalkolar kushalkolar mentioned this pull request Jul 21, 2024
add colorbar to hlut tool, add pause_events() context manager #550
Closed
clewis7
clewis7 reviewed Jul 23, 2024
View reviewed changes
Comment thread fastplotlib/graphics/_axes.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread fastplotlib/graphics/_axes.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread fastplotlib/graphics/_axes.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread fastplotlib/graphics/_axes.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread fastplotlib/graphics/_axes.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread fastplotlib/graphics/_base.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread fastplotlib/graphics/utils.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread fastplotlib/layouts/_figure.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

kushalkolar added 5 commits July 23, 2024 20:47
@kushalkolar
type annotations
cc427a0
@kushalkolar
fixes for gridplots
f780fa8
@kushalkolar
bbox account for camera state
5047e9f
@kushalkolar
change method name
e48f618
@kushalkolar
Ruler tick text mapping
3ad7575
@kushalkolar

kushalkolar commented Jul 24, 2024 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member Author

I subclassed pygfx.Ruler and added two attributes:

  • font_size, default 14
  • tick_text_mapper, default None

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:

image

using array_mapper:

image

A few caveats

Axes rendering is weird in docs gallery

But 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

image

issue 2, no axes in the iris scatter plots, probably zoomed out of far clipping plane (too zoomed in)

image

Test screenshots

does not render the grid through the entire canvas

image_simple

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

kushalkolar added 5 commits July 23, 2024 23:48
@kushalkolar
update docs
88201b0
@kushalkolar
docstring
f54558a
@kushalkolar
update docs
618d0de
@kushalkolar
set axes using scence bbox in Figure.show() for WgpuManualOffscreenCa…
03225bb
…nvas so axes are somewhat sensible
@kushalkolar
update year
44d1e3f
@kushalkolar @clewis7
black
5bbdec3
@clewis7
clewis7 force-pushed the axes branch from ada0dbe to 5bbdec3 Compare July 25, 2024 13:43
clewis7
clewis7 previously approved these changes Jul 25, 2024
View reviewed changes
Comment thread fastplotlib/graphics/_axes.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

kushalkolar added 3 commits July 25, 2024 20:19
@kushalkolar
add fastplotlib to intersphinx for examples links
a198686
@kushalkolar
don't test linear selector example because github action is being weird
266e03a
@kushalkolar
merge conflict
c845d33
@kushalkolar
kushalkolar dismissed clewis7’s stale review via c845d33 July 26, 2024 00:23
@kushalkolar

kushalkolar commented Jul 26, 2024 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member Author

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

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

kushalkolar added 2 commits July 25, 2024 20:53
@kushalkolar
set timeout of 10 mins for CI actions
4df9197
@kushalkolar
remove selectors from testutils
53c5229
@kushalkolar

kushalkolar commented Jul 26, 2024

Copy link
Copy Markdown
Member Author

I removed selectors from testutils for now, let's see

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@kushalkolar

kushalkolar commented Jul 26, 2024 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member Author

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.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

kushalkolar added 5 commits July 25, 2024 21:45
@kushalkolar
try something
abe5b91
@kushalkolar
try anotehr thing
221463e
@kushalkolar
add back axes update using camera
c36c112
@kushalkolar
try yet another thing
ccb3839
@kushalkolar
axes.update_using_camera in Figure.show() is fine, but is viewport.re…
0ff8982
…nder blocking
@kushalkolar

kushalkolar commented Jul 26, 2024

Copy link
Copy Markdown
Member Author

Seems like the viewport.render() call is blocking CI but only on github actions and not locally even with lavapipe

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

kushalkolar added 6 commits July 25, 2024 23:04
@kushalkolar
see if we can move the render call to test_examples.py
a7b208b
@kushalkolar
wrong attr
52a7ade
@kushalkolar
should work on rtd and CI test examples screenshots
c0d0db6
@kushalkolar
fix keyerror
44d6055
@kushalkolar
diffs are a bit larger now because of axes, increase tolerance and re…
1679c35
…move linear selector from tests because the diff is very large
@kushalkolar
update tolerances for nb test
2056ca3
@kushalkolar

kushalkolar commented Jul 26, 2024

Copy link
Copy Markdown
Member Author

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())
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@kushalkolar
forgot to update some test screenshots
fbac8ef
@kushalkolar
kushalkolar requested a review from clewis7 July 26, 2024 07:06
clewis7
clewis7 approved these changes Jul 26, 2024
View reviewed changes
@clewis7
clewis7 merged commit c27f489 into main Jul 26, 2024
@kushalkolar
kushalkolar deleted the axes branch July 26, 2024 14:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@almarklein almarklein almarklein left review comments
@clewis7 clewis7 clewis7 approved these changes

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

background

Uh oh!

There was an error while loading. Please reload this page.

3 participants

@kushalkolar @almarklein @clewis7
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.