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 — Voxels 3d colorizer v2 by trygvrad · Pull Request #31032 · matplotlib/matplotlib

Conversation

@trygvrad

@trygvrad trygvrad commented Jan 24, 2026

Copy link
Copy Markdown
Contributor

PR summary

This PR is in response to and closes #22969 [ENH]: Voxels as mappable for colorbar
The technical implementation builds upon the discussion in PR #30982 (closed)

This change allows axes3d.voxels() to take scalar data as input, and map it to color using a colorbar. The colors are applied to the voxels before shading.

The following code demonstrates the new functionality:

import numpy as np
import matplotlib.pyplot as plt

#Mock data
X,Y,Z = np.meshgrid(np.linspace(0,5,10), np.linspace(0,5,10), np.linspace(0,5,10), indexing="ij")
Hist = np.zeros((9,9,9))
Hist[2,5,5], Hist[5,5,3], Hist[5,7,5], Hist[5,0,5] = 1, 55, 4, 39
Hist[5,1,5] = 39
Hist[Hist == 0] = np.nan

# plot
fig=plt.figure()
ax = fig.add_subplot(projection='3d')
res = ax.voxels(X, Y, Z, Hist, cmap='rainbow', norm='log', alpha=0.5)
fig.colorbar(res)
res.colorizer.norm.vmin = 3
image

The technical implementation is such that:

  • scalar data can be input instead of boolean for the filled argument
  filled : 3D np.array of bool or float
       If bool, with truthy values indicate which voxels to fill.
       If float, voxels with finite values are shown with color
       mapped via *cmap* and *norm*, while voxels with nan are ignored.

Technical implementation

This technical implementation changes the return type of ax.voxel() from dict to the new class VoxelDict.
Where VoxelDict is valid input for fig.colorbar(), but VoxelDict is not itself an artist.

class VoxelDict(mcolorizer._ColorbarMappable,
                collections.abc.MutableMapping):

VoxelDict inherits from _ColorbarMappable, a new class introduced in the class hierarchy of ColorizingArtist (colorizer.py).
with these changes, this hierarchy is now:

class _ColorizerInterface:
    ...
class _ColorbarMappable(_ColorizerInterface):
    """
    Base class for objects that can connect to a colorbar.

    All classes that can act as a mappable for `fig.colorbar(mappable)`
    will subclass this class.
    """
    ...
class _ScalarMappable(_ColorbarMappable):
    ...
class ColorizingArtist(_ScalarMappable, artist.Artist):
    ...

I believe this change in the class hierarchy is suitable, because it makes explicit the requirements for colorbar.

Next step

First we need a discussion about the merits of the technical implementation.
Note that this implementation allows the user to set new data on existing voxels (which will update the colors), but it does not allow for adding/removing the voxels themselves [this will require more complicated methods, and can be implemented later].

If there is agreement that this implementation is suitable, I will need to add more tests.

  • "closes [ENH]: Voxels as mappable for colorbar #22969" is in the body of the PR description to link the related issue
  • new and changed code is tested
  • Plotting related features are demonstrated in an example
  • New Features and API Changes are noted with a directive and release note
  • Documentation complies with general and docstring guidelines

Sorry, something went wrong.

Uh oh!

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

All reactions
@trygvrad
VoxelDict class as return type for axes3d.Voxel
ecd8d56
@github-actions github-actions Bot added topic: mplot3d topic: color/color & colormaps labels Jan 24, 2026
@trygvrad trygvrad mentioned this pull request Jan 24, 2026
Voxels as mappable for colorbar & changed return type of voxels #30982
Closed
2 of 5 tasks
@trygvrad

trygvrad commented Jan 24, 2026

Copy link
Copy Markdown
Contributor Author

@timhoffm The implementation here is inspired by your comment #30982 (comment)

I realized _ColorizerInterface fulfills almost all the requirements for a mappable for colorbar, so instead of defining a protocol, It occured to me that it might be easier to simply define a new class that subclasses _ColorizerInterface. This greatly reduces the complexity of VoxelDict. Let me know what you think :)

All reactions

Sorry, something went wrong.

Uh oh!

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

@trygvrad
introduction of _ColorbarMappable
5f78c20
@trygvrad
trygvrad force-pushed the voxels_3d_colorizer_v2 branch from 5bba200 to 5f78c20 Compare January 25, 2026 00:08
@timhoffm

timhoffm commented Jan 25, 2026

Copy link
Copy Markdown
Member

@trygvrad I just had a quick look. Overall this looks reasonable.

I'm a bit worried on the deep nesting we create here:

class _ColorizerInterface:
class _ColorbarMappable(_ColorizerInterface):
class _ScalarMappable(_ColorbarMappable):
class ColorizingArtist(_ScalarMappable, artist.Artist):

Do we need all of these? Can we answer the questions: What exactly does each class bring additionally to the table? Why can't that functionality live in the parent or child class.

All reactions

Sorry, something went wrong.

Uh oh!

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

@trygvrad

trygvrad commented Jan 25, 2026

Copy link
Copy Markdown
Contributor Author

Do we need all of these? Can we answer the questions: What exactly does each class bring additionally to the table? Why can't that functionality live in the parent or child class.

Currently we have

  1. class _ColorizerInterface:
    Base class that contains the interface to Colorizer objects from a ColorizingArtist

  2. class _ScalarMappable(_ColorbarMappable):
    Legacy interface that may still be in use by third parties. This is available with import matplotlib.cm.ScalarMappable
    I would like to remove this at some point, and the class has a note as to how this can be done.

  3. class ColorizingArtist(_ScalarMappable, artist.Artist):
    Base class for all artist that use a colormap

In order to have a mappable that is not an artist, we need all the functionality of _ColorizerInterface and some of the functionality of _ScalarMappable. We can solve this by introducing a new class in the hierarchy, but a better option maybe to simply move some of the member functions from _ScalarMappable to _ColorizerInterface, as you @timhoffm suggest.


I would personally prefer _ColorbarMappable [or something similar] as a name for the _ColorizerInterface with increased functionality, as this describes what this class can actually be used for [input to fig.colorbar(...)].

I don't think there is currently any use case for _ColorizerInterface outside of ColorizingArtist

All reactions

Sorry, something went wrong.

Uh oh!

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

@timhoffm

timhoffm commented Jan 25, 2026

Copy link
Copy Markdown
Member

This sounds reasonable. We can always recreate a _ColorizerInterface at the top of the hierarchy later if needed.

Could you do these architectural changes as a separate PR? I believe it's easier to review/discuss the architecture without the voxels in between.

All reactions

Sorry, something went wrong.

Uh oh!

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

@trygvrad trygvrad mentioned this pull request Jan 26, 2026
introduction of _ColorbarMappable #31039
Open
4 tasks
@trygvrad

trygvrad commented Jan 26, 2026

Copy link
Copy Markdown
Contributor Author

@timhoffm please take a look at #31039 :)

All reactions

Sorry, something went wrong.

Uh oh!

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

scottshambaugh
scottshambaugh reviewed Feb 3, 2026
View reviewed changes

@scottshambaugh scottshambaugh left a comment

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This threads the needle much more cleanly than the last PR, thanks for putting it together. Could you please add a test with a new baseline image, as well as a whats new entry?

Sorry, something went wrong.

Uh oh!

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

All reactions
Comment thread lib/mpl_toolkits/mplot3d/axes3d.py
if filled.dtype == np.float64:
scalars = filled
filled = np.isfinite(filled)
#colorizer.autoscale_None(scalars[filled])

@scottshambaugh scottshambaugh Feb 3, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#colorizer.autoscale_None(scalars[filled])

Sorry, something went wrong.

Uh oh!

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

All reactions

@trygvrad trygvrad Feb 7, 2026

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @scottshambaugh
I will come back to this PR after we figure out #31039

Sorry, something went wrong.

Uh oh!

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

All reactions
@github-actions github-actions Bot added the status: needs rebase label Jul 27, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@scottshambaugh scottshambaugh scottshambaugh left review comments

At least 1 approving review is required to merge this pull request.

Uh oh!

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

Assignees

No one assigned

Labels

status: needs rebase topic: color/color & colormaps topic: mplot3d

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

[ENH]: Voxels as mappable for colorbar

Uh oh!

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

3 participants

@trygvrad @timhoffm @scottshambaugh
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.