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
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 20 additions & 12 deletions lib/mpl_toolkits/mplot3d/art3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -455,18 +455,27 @@ def do_3d_projection(self, renderer):

fcs = (zalpha(self._facecolor3d, vzs) if self._depthshade else
self._facecolor3d)
fcs = mcolors.to_rgba_array(fcs, self._alpha)
self.set_facecolors(fcs)

ecs = (zalpha(self._edgecolor3d, vzs) if self._depthshade else
self._edgecolor3d)

# 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.

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.


fcs = mcolors.to_rgba_array(fcs, self._alpha)
ecs = mcolors.to_rgba_array(ecs, self._alpha)

self.set_edgecolors(ecs)
PathCollection.set_offsets(self, np.column_stack([vxs, vys]))
self.set_facecolors(fcs)

PathCollection.set_offsets(self, vps)

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).

else :
else:
return np.nan


Expand Down Expand Up @@ -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 self._codes3d is not None:
codes = [self._codes3d[idx] for z, s, fc, ec, idx in z_segments_2d]
codes = [self._codes3d[idx] for idx in idxs]
PolyCollection.set_verts_and_codes(self, segments_2d, codes)
else:
PolyCollection.set_verts(self, segments_2d, self._closed)

self._facecolors2d = [fc for z, s, fc, ec, idx in z_segments_2d]
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.


# Return zorder value
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 24 additions & 0 deletions lib/mpl_toolkits/tests/test_mplot3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,30 @@ def test_scatter3d_color():
color='b', marker='s')


@image_comparison(baseline_images=['scatter3d_overlap_markers_yellow'],
extensions=['png'], remove_text=True)
def test_overlap_markers_yellow_first():
fig = plt.figure()
x = np.array([-1, 1])
y = np.array([1, -1])
z = np.array([0, 0])
ax = fig.add_subplot(111, projection='3d')
patches = ax.scatter(x, y, z, s=3500, c=['b', 'y'])
ax.view_init(elev=0, azim=-50)


@image_comparison(baseline_images=['scatter3d_overlap_markers_blue'],
extensions=['png'], remove_text=True)
def test_overlap_markers_blue_first():
fig = plt.figure()
x = np.array([-1, 1])
y = np.array([1, -1])
z = np.array([0, 0])
ax = fig.add_subplot(111, projection='3d')
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?

@image_comparison(baseline_images=['plot_3d_from_2d'], remove_text=True,
extensions=['png'])
def test_plot_3d_from_2d():
Expand Down