Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Add lightsource parameter to bar3d #15099

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

Merged
merged 13 commits into from
Aug 25, 2019

Conversation

fourpoints
Copy link
Contributor

@fourpoints fourpoints commented Aug 22, 2019

Bar3d now allows the user to define the user to define the origin of the lightsource.

PR Summary

Note: I have little experience with pull requests, and I have not thouroughly checked the correctness of the code (even though it is two lines of code, anything can go wrong), so I cannot advise a merge just yet. I would like to hear if this change is even needed (is this even the best way to go about it?). I will make the proper adjustments if needed, any advice is appreciated.

This adds a lightsource parameter to bar3d. This is already a documented feature without any implementation (AFAIK). The documentation for the shade parameter says:

When true, this shades the dark sides of the bars (relative to the plot's source of light).

However, there is currently no apparent way to set this, as bar3d calls self._shade_colors(facecolors, normals) without a lightsource parameter, which by default uses LightSource(azdeg=225, altdeg=19.4712) if no lightsource parameter is set.

PR Checklist

  • Has Pytest style unit tests
  • Code is Flake 8 compliant
  • New features are documented, with examples if plot related
  • Documentation is sphinx and numpydoc compliant
  • Added an entry to doc/users/next_whats_new/ if major new feature (follow instructions in README.rst there)
  • Documented in doc/api/api_changes.rst if API changed in a backward-incompatible way

Bar3d now allows the user to define the user to define the origin of the lightsource.
@@ -2375,6 +2375,9 @@ def bar3d(self, x, y, z, dx, dy, dz, color=None,
shade : bool, optional (default = True)
When true, this shades the dark sides of the bars (relative
to the plot's source of light).

lightsource : matplotlib.color.LightSource, optional (default = None)
The lightsource defines the plot's source of light.

Choose a reason for hiding this comment

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

The lightsource defines the plot's source of light, if shade=True is set.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks, I noticed that plot_trisurf already uses this parameter so I copied its line instead:

lightsource : `~matplotlib.colors.LightSource`
The lightsource to use when *shade* is True.

Uses `plot_trisurface`'s documentation for `bar3d`'s lightsource parameter.
Failed Travis CI test for flake8.
@ImportanceOfBeingErnest
Copy link
Member

In general it would be nice to see the effect of this new parameter. Do you have a small example code with an image you can show in the PR?

Also there should be a test that checks that the new parameter does what it should. In the simplest case this can just be a smoke-test, or one could check if a particular face of the bar really has the expected color.

Fixed over- and under-indentation.
@fourpoints
Copy link
Contributor Author

fourpoints commented Aug 22, 2019

Demo

sample code

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.colors as mcolors
import numpy as np

fig = plt.figure()
ax = fig.add_subplot(1, 1, 1, projection="3d")

ls = mcolors.LightSource(azdeg=0, altdeg=90)
cmap = plt.get_cmap("coolwarm")

length, width = 3, 4
area = length * width

norm = mcolors.Normalize(0, area-1)

x, y = np.meshgrid(np.arange(length), np.arange(width))
x = x.ravel()
y = y.ravel()
dz = x + y

color = np.array([cmap(norm(i)) for i in range(area)])

collection = ax.bar3d(x=x, y=y, z=0,
         dx=1, dy=1, dz=dz,
         color=color, shade=True, lightsource=ls)

ax.set_title(f"Azimutal degree = {ls.azdeg}°, altitude degree = {ls.altdeg}°")

plt.show()

# simple color test
assert np.all(color == collection._facecolors3d[1::6]),\
    "bar colors doesn't match"

Issues:

  • I am still not sure why Travis CI is failing. Apparently due to flake8, but this I cannot reproduce.
  • I am not sure where I am supposed to add the test?

@@ -2375,6 +2375,9 @@ def bar3d(self, x, y, z, dx, dy, dz, color=None,
shade : bool, optional (default = True)
When true, this shades the dark sides of the bars (relative
to the plot's source of light).

Choose a reason for hiding this comment

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

This is the line that causes the flake hickup. A blank like cannot contain any whitespaces.

@ImportanceOfBeingErnest
Copy link
Member

A test would need to go into lib/mpl_toolkits/tests/test_mplot3d.py; there are already 3 test for bars.

Compares the computed colors of the bar tops with the user-defined colors.
dx=1, dy=1, dz=dz,
color=color, shade=True, lightsource=ls)

np.testing.assert_array_equal(color, collection._facecolors3d[1::6])

Choose a reason for hiding this comment

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

What I don't understand here is, if the facecolors are actually the same as the colors taken from the colormap, are we really testing that the lightsource works? That is, if there is a lightsource, shouldn't the colors be different from the ones from the coolwarm colormap?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

._facecolors3d[1::6] should be taking the color of the top surfaces, which with a lightsource coming from the top (altdeg=90), should leave these unchanged (fully illuminated), as expected.

Changing the altdeg (e.g. default value of 19.4712) or selecting different sides (e.g. .facecolors3d[2::6]) fails this test.

A test could be made which computes to expected colors given different angles, or compares to an image, but I thought this simple approach would suffice, as it ensures that the default lightsource is not used.

It's worth mentioning that the test would also pass if no shading were done, but test_bar3d_shaded should already test for this.

Choose a reason for hiding this comment

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

I see. So imagine in the future someone unintentionally removes lightsource from the line self._shade_colors(facecolors, normals, lightsource). The test would not detect this, right? But that is what we have tests for.

Copy link
Contributor Author

@fourpoints fourpoints Aug 23, 2019

Choose a reason for hiding this comment

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

The test would detect this.

Calling self._shade_colors(facecolors, normals) without a lightsource argument defaults to LightSource(azdeg=225, altdeg=19.4712), which would fail the test. This would shade the top surfaces with a different color. It will only pass if the top colors are unchanged, which requires a LightSource(altdeg=90) to be passed through.

Choose a reason for hiding this comment

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

Oh, that is not very obvious. I'd suggest to add that as a comment in the test; else I fear in the future people might be as confused as I was by it.

Copy link
Member

@ImportanceOfBeingErnest ImportanceOfBeingErnest Aug 25, 2019

Choose a reason for hiding this comment

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

May I suggest the following as comment in the test:

# Testing that the custom 90° lightsource produces different shading than
# the default, and that those colors are precisely the colors from the
# colormap, due to the illumination parallel to the z-axis.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks, that sounds much better. I also added that it compares the top facecolors.

#(...) different shading on the top facecolors compared to the default (...)

@ImportanceOfBeingErnest
Copy link
Member

One final thing. If you think this is a notable feature, people should be made be aware of, you can add a "What's new entry"

@tacaswell tacaswell added this to the v3.3.0 milestone Aug 24, 2019
Make the comment more obvious
@ImportanceOfBeingErnest ImportanceOfBeingErnest merged commit 1cf7539 into matplotlib:master Aug 25, 2019
@fourpoints fourpoints deleted the patch-2 branch August 25, 2019 19:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants
Morty Proxy This is a proxified and sanitized view of the page, visit original site.