-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Fix marker overlap #10791
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
Fix marker overlap #10791
Conversation
Sort points by z coordinate values similar to Poly3DCollection
This likely needs a test, right? |
@jklymak Yes, I will be updating this PR with an image comparison test later. Still looking into how to make one. |
|
||
# Sort the points based on z coordinates | ||
# Put vzs first to sort based on z value | ||
z_markers = sorted(zip(vzs, np.column_stack([vxs, vys]), fcs, ecs), |
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.
I'm a bit worried that this could slow down the rendering when using many patches.
I know it's bad practice to base such an assumption on pure gut feeling. But before I'm going into any tests. Have you done benchmarking on the performance impact?
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.
Yes I had ran some benchmarking beforehand. I didn't notice any noticeable impact on performance.
I used the following script
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import random as rand
SIZE=10000
x = np.array([rand.uniform(-50, 50) for i in range(SIZE)])
y = np.array([rand.uniform(-50, 50) for i in range(SIZE)])
z = np.array([rand.uniform(-50, 50) for i in range(SIZE)])
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
patches = ax.scatter(x, y, z, s=100, c=x)
plt.savefig("tmp.png")
Ran the script 5 times both before and after fix and didn't find any noticeable differences.
perf_fix.log
perf_vanilla.log
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.
These timings would have been swamped by all of the imports and figure creation and such. A real benchmark would involve seeing how long it would take to set up the plot, and then have it rotate around, like in one of the animation examples.
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.
I did a quick check on this part of the code, with numbers of points increased to 1000 000 (I think 100x100x100 is not unrealistic). With that the present sorting/zipping implementation is in the 100ms regime.
That's a size which gets relevant for performance in interactive use.
In a quick test, it's possible to get a roughly 10x speedup by using numpy.argsort
on vzs and applying the index array to the other quantities.
lib/mpl_toolkits/mplot3d/art3d.py
Outdated
# Put vzs first to sort based on z value | ||
z_markers = sorted(zip(vzs, np.column_stack([vxs, vys]), fcs, ecs), | ||
reverse=True) | ||
vps = [ps for zs, ps, fc, ec in z_markers] |
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.
Can you just do:
[zzs, vps, fcs, ecs] = zip(*zmarkers)
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.
I knew there should've been an easier way to do that. I did not know about the starred expression syntax though. I have also changed up Poly3DCollection's do_3d_projection method to use this.
|
||
# Sort the points based on z coordinates | ||
# Put vzs first to sort based on z value | ||
z_markers = sorted(zip(vzs, np.column_stack([vxs, vys]), fcs, ecs), |
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.
These timings would have been swamped by all of the imports and figure creation and such. A real benchmark would involve seeing how long it would take to set up the plot, and then have it rotate around, like in one of the animation examples.
z_markers = sorted(zip(vzs, np.column_stack([vxs, vys]), fcs, ecs), | ||
reverse=True) | ||
|
||
[zzs, vps, fcs, ecs] = zip(*z_markers) |
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.
you'll need a check for an empty z_markers
before trying to doing this.
|
||
if vzs.size > 0 : | ||
if vzs.size > 0: | ||
return min(vzs) |
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.
we don't need to do min(vzs)
anymore. we now have the sorted zzs
. Just return the last element (since you did a reversed sort).
@@ -634,17 +643,16 @@ def do_3d_projection(self, renderer): | |||
else: | |||
raise ValueError("whoops") | |||
|
|||
segments_2d = [s for z, s, fc, ec, idx in z_segments_2d] | |||
[zzs, segments_2d, self._facecolors2d, self._edgecolors2d, idxs] = \ | |||
zip(*z_segments_2d) |
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.
another check for empty z_segments_2d
before doing this.
if len(self._edgecolors3d) == len(cface): | ||
self._edgecolors2d = [ec for z, s, fc, ec, idx in z_segments_2d] | ||
else: | ||
if len(self._edgecolors3d) != len(cface): | ||
self._edgecolors2d = self._edgecolors3d |
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.
edgecolors3d hasn't been sorted, if I understand this correctly.
patches = ax.scatter(x, y, z, s=3500, c=['b', 'y']) | ||
ax.view_init(elev=0, azim=130) | ||
|
||
|
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.
Can you add a bit more to these tests? Make sure the edge colors are getting sorted properly, too?
@xenoryt Are you going to continue with this? As mentioned, I've found that using |
Thanks a lot for the contribution. I'm going to close this as having had no action for quite a while. OTOH, if there is still a community that wants this, please feel free to re-open, with my apologies! |
PR Summary
Fixes issue addressed in addressed in #5830 with markers overlapping incorrectly.
Fixed by sorting markers by z coordinate values similar to Poly3DCollection so that closest ones are drawn last.
PR Checklist