Skip to content

Arrow head color fix to match arrow body #13742

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

Closed
wants to merge 2 commits into from

Conversation

raunM
Copy link

@raunM raunM commented Mar 23, 2019

PR Summary

Tried to fix this issue by separating arrow head bodies and arrow heads to be used for generating gradient lines separately. This PR is part of coursework for CSCD01 at UTSC.

Closes #11759

Bug Description

Currently when plotting arrows using the quiver method with a gradient correctly shows a gradient for arrow bodies but not for arrow heads. Within the quiver method inside class Axes3D in mpl_toolkits/mplot3d/axes3d.py each arrow head is actually created as two lines that are at a different angle to the body of the arrow. Originally the main body lines and the lines used to generate the arrow heads are being passed together to create a single Line3DCollection object:

lines = [*shafts, *heads]
.
.
.
linec = art3d.Line3DCollection(lines, *args[argi:], **kwargs)

This resulted in the arrow heads to have an inconsistent gradient for the plot. The fix was to create separate Line3DCollection for each collection of lines. Instead of one single Line3DCollection object to be plotted containing the body lines and then the arrow head lines, I now split it into a Line3DCollection object for the body lines, a Line3DCollection object for one side of the arrow heads, and a Line3DCollection object for the other side of the arrow
heads.

Example code to test

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

x = np.zeros(10)
y = np.zeros(10)
z = np.arange(10.)
dx = np.zeros(10)
dy = np.arange(10.)
dz = np.zeros(10)

fig = plt.figure()
ax = fig.gca(projection='3d')

arrow_color = plt.cm.Reds(dy/dy.max())

ax.quiver(x, y, z, dx, dy, dz, colors=arrow_color)
ax.set_ylim(0,10)
plt.show()

Before

before

After

arrowheads

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

raunM added 2 commits March 23, 2019 00:40
Seperated arrow head bodies and arrow heads to be used for generating gradient lines seperately.
Seperated arrow head bodies and arrow heads to be used for generating gradient lines seperately.
@timhoffm
Copy link
Member

Thanks for going forward with trying to solve this issue.

The problem with using three line collections is that the return value of Axes3D.quiver() is a single line collection. The proposed solution only returns the collection of the shafts, which breaks the API. Returning separate collections is not really better as that would break the existing API and on top it's not a good API to have three separate collections for the API.

Not really sure, what the right way to go is:

  • One could replicate colors so that [c1, c2] would be transformed to [c1, c1, c1, c2, c2, c2] before passing to the line collection.
  • One could change the LineCollection to a PolyCollection and draw each arrow with a single stroke. This is also an API change.
  • One could simple disallow multiple colors for the color parameter in Axes3D.quiver().

Maybe other devs have more ideas.

@raunM
Copy link
Author

raunM commented Mar 23, 2019

Thanks for looking at my solution. What about also having a single line collection of all the lines that can be used just for returning purposes? This would essentially be the same as what was being done before.

@jklymak jklymak changed the title Arrow head color fix to match arrow body for issue #11759 Arrow head color fix to match arrow body Mar 23, 2019
@timhoffm
Copy link
Member

What about also having a single line collection of all the lines that can be used just for returning purposes?

That's basically my first bullet point. Since you have [shaft1, arrowleft1, arrowright1, shaft2, arrowleft2, arrowright2, ...], you need to have the first color three times, then the second color three times and so on.

Note that it will be a bit tricky to implement this correctly. Color-replication should only be done if there are multiple colors - i.e. not if e.g. colors='green'. But colors can have many (including list-like) formats: https://matplotlib.org/api/colors_api.html#module-matplotlib.colors. So deciding if this is a single color or multiple colors is not simple.

Note also, that the same problem will arise for other arguments, e.g. linestyles=['--', ':'], making a complete solution using replication even more cumbersome.

@jklymak jklymak marked this pull request as draft April 15, 2021 18:03
@jklymak
Copy link
Member

jklymak commented Apr 15, 2021

This looked on the right track...

@scottshambaugh
Copy link
Contributor

This PR is stale and the issue has been addressed by #27754, so closing this one.

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.

The color of the 3D arrow head does not match that of the arrow body
4 participants