X Tutup
Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
2 changes: 2 additions & 0 deletions lib/matplotlib/colorbar.py
Original file line number Diff line number Diff line change
Expand Up @@ -1043,6 +1043,8 @@ def remove(self):

try:
ax = self.mappable.axes
if ax is None:
return
except AttributeError:
return
try:
Expand Down
73 changes: 47 additions & 26 deletions lib/matplotlib/colorizer.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""
The Colorizer class which handles the data to color pipeline via a
normalization and a colormap.
Expand Down Expand Up @@ -514,7 +514,51 @@
return g_sig_digits


class _ScalarMappable(_ColorizerInterface):
class _ColorbarMappable(_ColorizerInterface):

def __init__(self, colorizer, **kwargs):
"""
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.
"""
super().__init__(**kwargs)
self._colorizer = colorizer
self.colorbar = None
self._id_colorizer = self._colorizer.callbacks.connect('changed', self.changed)
self.callbacks = cbook.CallbackRegistry(signals=["changed"])
self._axes = None

@property
def axes(self):
return self._axes

@axes.setter
def axes(self, axes):
self._axes = axes

@property
def colorizer(self):
return self._colorizer

@colorizer.setter
def colorizer(self, cl):
_api.check_isinstance(Colorizer, colorizer=cl)
self._colorizer.callbacks.disconnect(self._id_colorizer)
self._colorizer = cl
self._id_colorizer = cl.callbacks.connect('changed', self.changed)

def changed(self):
"""
Call this whenever the mappable is changed to notify all the
callbackSM listeners to the 'changed' signal.
"""
self.callbacks.process('changed')
self.stale = True


class _ScalarMappable(_ColorbarMappable):
"""
A mixin class to map one or multiple sets of scalar data to RGBA.

Expand Down Expand Up @@ -550,13 +594,9 @@
cmap : str or `~matplotlib.colors.Colormap`
The colormap used to map normalized data values to RGBA colors.
"""
super().__init__(**kwargs)
self._A = None
self._colorizer = self._get_colorizer(colorizer=colorizer, norm=norm, cmap=cmap)

self.colorbar = None
self._id_colorizer = self._colorizer.callbacks.connect('changed', self.changed)
self.callbacks = cbook.CallbackRegistry(signals=["changed"])
colorizer = self._get_colorizer(colorizer=colorizer, norm=norm, cmap=cmap)
super().__init__(colorizer, **kwargs)

def set_array(self, A):
"""
Expand Down Expand Up @@ -600,14 +640,6 @@
"""
return self._A

def changed(self):
"""
Call this whenever the mappable is changed to notify all the
callbackSM listeners to the 'changed' signal.
"""
self.callbacks.process('changed', self)
self.stale = True

@staticmethod
def _check_exclusionary_keywords(colorizer, **kwargs):
"""
Expand Down Expand Up @@ -710,17 +742,6 @@
_api.check_isinstance(Colorizer, colorizer=colorizer)
super().__init__(colorizer=colorizer, **kwargs)

@property
def colorizer(self):
return self._colorizer

@colorizer.setter
def colorizer(self, cl):
_api.check_isinstance(Colorizer, colorizer=cl)
self._colorizer.callbacks.disconnect(self._id_colorizer)
self._colorizer = cl
self._id_colorizer = cl.callbacks.connect('changed', self.changed)

def _set_colorizer_check_keywords(self, colorizer, **kwargs):
"""
Raises a ValueError if any kwarg is not None while colorizer is not None.
Expand Down
22 changes: 19 additions & 3 deletions lib/matplotlib/colorizer.pyi
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from matplotlib import cbook, colorbar, colors, artist
from matplotlib import cbook, colorbar, colors, artist, axes as maxes

import numpy as np
from numpy.typing import ArrayLike
Expand Down Expand Up @@ -71,7 +71,24 @@ class _ColorizerInterface:
def autoscale_None(self) -> None: ...


class _ScalarMappable(_ColorizerInterface):
class _ColorbarMappable(_ColorizerInterface):
def __init__(
self,
colorizer: Colorizer | None,
**kwargs
) -> None: ...
@property
def colorizer(self) -> Colorizer: ...
@colorizer.setter
def colorizer(self, cl: Colorizer) -> None: ...
def changed(self) -> None: ...
@property
def axes(self) -> maxes._base._AxesBase | None: ...
@axes.setter
def axes(self, new_axes: maxes._base._AxesBase | None) -> None: ...


class _ScalarMappable(_ColorbarMappable):
def __init__(
self,
norm: colors.Norm | None = ...,
Expand All @@ -82,7 +99,6 @@ class _ScalarMappable(_ColorizerInterface):
) -> None: ...
def set_array(self, A: ArrayLike | None) -> None: ...
def get_array(self) -> np.ndarray | None: ...
def changed(self) -> None: ...


class ColorizingArtist(_ScalarMappable, artist.Artist):
Expand Down
111 changes: 107 additions & 4 deletions lib/mpl_toolkits/mplot3d/art3d.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# art3d.py, original mplot3d version by John Porter
# Parts rewritten by Reinier Heeres <reinier@heeres.eu>
# Minor additions by Ben Axelrod <baxelrod@coroware.com>
Expand All @@ -15,11 +15,12 @@

from matplotlib import (
_api, artist, cbook, colors as mcolors, lines, text as mtext,
path as mpath, rcParams)
path as mpath, rcParams, colorizer as mcolorizer)
from matplotlib.collections import (
Collection, LineCollection, PolyCollection, PatchCollection, PathCollection)
from matplotlib.patches import Patch
from . import proj3d
import collections


def _norm_angle(a):
Expand Down Expand Up @@ -1206,22 +1207,26 @@
and _edgecolors properties.
"""
if shade:
normals = _generate_normals(verts)
self.shaded = True
self.normals = _generate_normals(verts)
self.lightsource = lightsource
facecolors = kwargs.get('facecolors', None)
if facecolors is not None:
kwargs['facecolors'] = _shade_colors(
facecolors, normals, lightsource
facecolors, self.normals, self.lightsource
)

edgecolors = kwargs.get('edgecolors', None)
if edgecolors is not None:
kwargs['edgecolors'] = _shade_colors(
edgecolors, normals, lightsource
edgecolors, self.normals, self.lightsource
)
if facecolors is None and edgecolors is None:
raise ValueError(
"You must provide facecolors, edgecolors, or both for "
"shade to work.")
else:
self.shaded = False
super().__init__(verts, *args, **kwargs)
if isinstance(verts, np.ndarray):
if verts.ndim != 3:
Expand All @@ -1233,6 +1238,37 @@
self._codes3d = None
self._axlim_clip = axlim_clip

def update_scalarmappable(self):
"""
Update colors from the scalar mappable array, if any.

This overrides `Collection.update_scalarmappable()`.
This function differs in the following way:
1. This function only sets facecolors, never edgecolors
2. This function applies shading.
3. self._A is assumed to have the correct shape
"""
if not self._set_mappable_flags():
return
# Allow possibility to call 'self.set_array(None)'.
if self._A is not None:
if np.iterable(self._alpha):
if self._alpha.size != self._A.size:
raise ValueError(
f'Data array shape, {self._A.shape} '
'is incompatible with alpha array shape, '
f'{self._alpha.shape}. '
)
self._alpha = self._alpha.reshape(self._A.shape)
self._mapped_colors = self.to_rgba(self._A, self._alpha)
if self.shaded:
self._mapped_colors = _shade_colors(
self._mapped_colors, self.normals, self.lightsource
)

self._facecolors = self._mapped_colors
self.stale = True

_zsort_functions = {
'average': np.average,
'min': np.min,
Expand Down Expand Up @@ -1675,3 +1711,70 @@
colors = np.asanyarray(color).copy()

return colors


class VoxelDict(mcolorizer._ColorbarMappable,
collections.abc.MutableMapping):
"""
A mapping indexed by coordinate, where ``faces[i, j, k]``
is a `.Poly3DCollection` of the faces drawn for the voxel
``filled[i, j, k]``. If no faces were drawn for a given voxel,
either because it was not asked to be drawn, or it is fully
occluded, then ``(i, j, k) not in faces``.

This class also supports the functionality required to act as a mappable
for a colorbar.
"""

def __init__(self, colorizer, axes):
"""
Parameters
----------
colorizer : `mpl.colorizer.Colorizer`
The colorizer uset to convert data to color.

axes : `mplot3d.axes3d.Axes3D`
The axes the voxels are contained in.

"""

super().__init__(colorizer)
self.axes = axes
self._A = None
self._mapping = dict()

def __getitem__(self, key):
return self._mapping[key]

def __setitem__(self, key, val):
self._mapping[key] = val

def __delitem__(self, key):
del self._mapping[key]

def __len__(self):
return len(self._mapping)

def __iter__(self):
return reversed(self._mapping)

def get_array(self):
"""
Return the array of values, that are mapped to colors.
"""
return self._A

def set_array(self, A):
"""
Set the value array from array-like *A*.

Parameters
----------
A : array-like of length equal to the number of voxels.
The values that are mapped to colors.

"""

self._A = A
for a, k in zip(A, self.keys()):
self[k].set_array(a)
Loading
Loading
X Tutup