Thanks to visit codestin.com
Credit goes to github.com

Skip to content

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

Closed
wants to merge 4 commits into from
Closed

Conversation

xenoryt
Copy link
Contributor

@xenoryt xenoryt commented Mar 15, 2018

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

  • Has Pytest style unit tests
  • Code is PEP 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

Sort points by z coordinate values similar to Poly3DCollection
@jklymak
Copy link
Member

jklymak commented Mar 15, 2018

This likely needs a test, right?

@xenoryt
Copy link
Contributor Author

xenoryt commented Mar 15, 2018

@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),
Copy link
Member

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?

Copy link
Contributor Author

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

Copy link
Member

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.

Copy link
Member

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.

# 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]
Copy link
Member

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)

Copy link
Contributor Author

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.

@jklymak jklymak requested a review from WeatherGod March 24, 2018 20:37
@jklymak jklymak added this to the v3.0 milestone Mar 24, 2018

# 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),
Copy link
Member

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)
Copy link
Member

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)
Copy link
Member

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)
Copy link
Member

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
Copy link
Member

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)


Copy link
Member

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?

@timhoffm
Copy link
Member

timhoffm commented May 6, 2018

@xenoryt Are you going to continue with this? As mentioned, I've found that using numpy.argsort is significantly faster. You are welcome to update your PR with this. Alternatively, I can open a concurrent PR.

@tacaswell tacaswell modified the milestones: v3.0, v3.1 Jul 7, 2018
@jklymak jklymak modified the milestones: v3.1.0, v3.2.0 Feb 9, 2019
@jklymak jklymak added the stale label Feb 9, 2019
@jklymak jklymak modified the milestones: v3.2.0, unassigned Feb 9, 2019
@jklymak
Copy link
Member

jklymak commented Jul 16, 2019

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!

@jklymak jklymak closed this Jul 16, 2019
@jakeprojects jakeprojects mentioned this pull request Nov 2, 2019
6 tasks
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.

8 participants