|
11 | 11 | importnumpyasnp
|
12 | 12 |
|
13 | 13 | importmatplotlibasmpl
|
14 |
| -from .import_api,cbook |
15 |
| -from .colorsimportBoundaryNorm,BivarColormap |
16 |
| -from .cmimportVectorMappable |
| 14 | +from .import_api,cbook,cm |
| 15 | +from .cmimportScalarMappable |
17 | 16 | from .pathimportPath
|
18 | 17 | from .transformsimport (BboxBase,Bbox,IdentityTransform,Transform,TransformedBbox,
|
19 | 18 | TransformedPatchPath,TransformedPath)
|
@@ -1327,49 +1326,16 @@ def format_cursor_data(self, data):
|
1327 | 1326 | --------
|
1328 | 1327 | get_cursor_data
|
1329 | 1328 | """
|
1330 |
| -ifisinstance(self,VectorMappable)and \ |
1331 |
| - (np.ndim(data)==0orlen(data)==self.cmap.n_variates): |
1332 |
| -# This block logically belongs to VectorMappable, but can't be |
1333 |
| -# implemented in it because most VectorMappable subclasses |
1334 |
| -# inherit from Artist first and from VectorMappable second, so |
| 1329 | +ifisinstance(self,ScalarMappable): |
| 1330 | +# Internal classes no longer inherit from ScalarMappable, and this |
| 1331 | +# block should never be executed by the internal API |
| 1332 | + |
| 1333 | +# This block logically belongs to ScalarMappable, but can't be |
| 1334 | +# implemented in it in case custom ScalarMappable subclasses |
| 1335 | +# inherit from Artist first and from ScalarMappable second, so |
1335 | 1336 | # Artist.format_cursor_data would always have precedence over
|
1336 |
| -# VectorMappable.format_cursor_data. |
1337 |
| -ifself.cmap.n_variates==1: |
1338 |
| -data= [data] |
1339 |
| -# The above if test is equivalent to `isinstance(self.cmap, Colormap)` |
1340 |
| -num_colors= [self.cmap.N] |
1341 |
| -else: |
1342 |
| -ifisinstance(self.cmap,BivarColormap): |
1343 |
| -num_colors= [self.cmap.N,self.cmap.M] |
1344 |
| -else:# i.e. a MultivarColormap object |
1345 |
| -num_colors= [component.Nforcomponentinself.cmap] |
1346 |
| - |
1347 |
| -out_str='[' |
1348 |
| -fornn,dd,ncinzip(self.nac._norm,data,num_colors): |
1349 |
| -ifnp.ma.getmask(dd): |
1350 |
| -out_str+=", " |
1351 |
| -else: |
1352 |
| -# Figure out a reasonable amount of significant digits |
1353 |
| -normed=nn(dd) |
1354 |
| -ifnp.isfinite(normed): |
1355 |
| -ifisinstance(nn,BoundaryNorm): |
1356 |
| -# not an invertible normalization mapping |
1357 |
| -cur_idx=np.argmin(np.abs(nn.boundaries-dd)) |
1358 |
| -neigh_idx=max(0,cur_idx-1) |
1359 |
| -# use max diff to prevent delta == 0 |
1360 |
| -delta=np.diff( |
1361 |
| -nn.boundaries[neigh_idx:cur_idx+2] |
1362 |
| - ).max() |
1363 |
| -else: |
1364 |
| -# Midpoints of neighboring color intervals. |
1365 |
| -neighbors=nn.inverse( |
1366 |
| - (int(normed*nc)+np.array([0,1]))/nc) |
1367 |
| -delta=abs(neighbors-dd).max() |
1368 |
| -g_sig_digits=cbook._g_sig_digits(dd,delta) |
1369 |
| -else: |
1370 |
| -g_sig_digits=3# Consistent with default below. |
1371 |
| -out_str+=f"{dd:-#.{g_sig_digits}g}, " |
1372 |
| -returnout_str[:-2]+']' |
| 1337 | +# ScalarMappable.format_cursor_data. |
| 1338 | +returnself.mapper._format_cursor_data(data) |
1373 | 1339 | else:
|
1374 | 1340 | try:
|
1375 | 1341 | data[0]
|
@@ -1412,6 +1378,115 @@ def set_mouseover(self, mouseover):
|
1412 | 1378 | mouseover=property(get_mouseover,set_mouseover)# backcompat.
|
1413 | 1379 |
|
1414 | 1380 |
|
| 1381 | +classColorableArtist(Artist): |
| 1382 | +def__init__(self,norm=None,cmap=None): |
| 1383 | +""" |
| 1384 | + Parameters |
| 1385 | + ---------- |
| 1386 | + norm : `.Normalize` (or subclass thereof) or str or None |
| 1387 | + The normalizing object which scales data, typically into the |
| 1388 | + interval ``[0, 1]``. |
| 1389 | + If a `str`, a `.Normalize` subclass is dynamically generated based |
| 1390 | + on the scale with the corresponding name. |
| 1391 | + If *None*, *norm* defaults to a *colors.Normalize* object which |
| 1392 | + initializes its scaling based on the first data processed. |
| 1393 | + cmap : str or `~matplotlib.colors.Colormap` |
| 1394 | + The colormap used to map normalized data values to RGBA colors. |
| 1395 | + """ |
| 1396 | + |
| 1397 | +Artist.__init__(self) |
| 1398 | + |
| 1399 | +self._A=None |
| 1400 | +ifisinstance(norm,cm.Mapper): |
| 1401 | +self._mapper=norm |
| 1402 | +else: |
| 1403 | +self._mapper=cm.Mapper(cmap,norm) |
| 1404 | + |
| 1405 | +self._id_mapper=self.mapper.callbacks.connect('changed',self.changed) |
| 1406 | +self.callbacks=cbook.CallbackRegistry(signals=["changed"]) |
| 1407 | + |
| 1408 | +defset_array(self,A): |
| 1409 | +""" |
| 1410 | + Set the value array from array-like *A*. |
| 1411 | +
|
| 1412 | + Parameters |
| 1413 | + ---------- |
| 1414 | + A : array-like or None |
| 1415 | + The values that are mapped to colors. |
| 1416 | +
|
| 1417 | + The base class `.VectorMappable` does not make any assumptions on |
| 1418 | + the dimensionality and shape of the value array *A*. |
| 1419 | + """ |
| 1420 | +ifAisNone: |
| 1421 | +self._A=None |
| 1422 | +return |
| 1423 | +A=cm._ensure_multivariate_data(self.cmap.n_variates,A) |
| 1424 | + |
| 1425 | +A=cbook.safe_masked_invalid(A,copy=True) |
| 1426 | +ifnotnp.can_cast(A.dtype,float,"same_kind"): |
| 1427 | +ifA.dtype.fieldsisNone: |
| 1428 | +raiseTypeError(f"Image data of dtype{A.dtype} cannot be " |
| 1429 | +f"converted to float") |
| 1430 | +else: |
| 1431 | +forkeyinA.dtype.fields: |
| 1432 | +ifnotnp.can_cast(A[key].dtype,float,"same_kind"): |
| 1433 | +raiseTypeError(f"Image data of dtype{A.dtype} cannot be " |
| 1434 | +f"converted to a sequence of floats") |
| 1435 | +self._A=A |
| 1436 | +self.mapper.autoscale_None(A) |
| 1437 | + |
| 1438 | +@property |
| 1439 | +defmapper(self): |
| 1440 | +returnself._mapper |
| 1441 | + |
| 1442 | +@mapper.setter |
| 1443 | +defmapper(self,mapper): |
| 1444 | +self._set_mapper(mapper) |
| 1445 | + |
| 1446 | +def_set_mapper(self,mapper): |
| 1447 | +ifisinstance(mapper,cm.Mapper): |
| 1448 | +ifself._AisnotNone: |
| 1449 | +ifnotmapper.n_variates==self.mapper.n_variates: |
| 1450 | +raiseValueError('The new Mapper object must have the same' |
| 1451 | +' number of variates as the existing data.') |
| 1452 | +else: |
| 1453 | +self.mapper.callbacks.disconnect(self._id_mapper) |
| 1454 | +self._mapper=mapper |
| 1455 | +self._id_mapper=mapper.callbacks.connect('changed',self.changed) |
| 1456 | +self.changed() |
| 1457 | +else: |
| 1458 | +raiseValueError('Only a Mapper object can be set to mapper.') |
| 1459 | + |
| 1460 | +defget_array(self): |
| 1461 | +""" |
| 1462 | + Return the array of values, that are mapped to colors. |
| 1463 | +
|
| 1464 | + The base class `.VectorMappable` does not make any assumptions on |
| 1465 | + the dimensionality and shape of the array. |
| 1466 | + """ |
| 1467 | +returnself._A |
| 1468 | + |
| 1469 | +defchanged(self): |
| 1470 | +""" |
| 1471 | + Call this whenever the mappable is changed to notify all the |
| 1472 | + callbackSM listeners to the 'changed' signal. |
| 1473 | + """ |
| 1474 | +self.callbacks.process('changed') |
| 1475 | +self.stale=True |
| 1476 | + |
| 1477 | +defformat_cursor_data(self,data): |
| 1478 | +""" |
| 1479 | + Return a string representation of *data*. |
| 1480 | +
|
| 1481 | + Uses the colorbar's formatter to format the data. |
| 1482 | +
|
| 1483 | + See Also |
| 1484 | + -------- |
| 1485 | + get_cursor_data |
| 1486 | + """ |
| 1487 | +returnself.mapper._format_cursor_data(data) |
| 1488 | + |
| 1489 | + |
1415 | 1490 | def_get_tightbbox_for_layout_only(obj,*args,**kwargs):
|
1416 | 1491 | """
|
1417 | 1492 | Matplotlib's `.Axes.get_tightbbox` and `.Axis.get_tightbbox` support a
|
|