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
Fixed polygon3d rendering bug issue #178 #1928
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -132,11 +132,13 @@ def path_to_3d_segment(path, zs=0, zdir='z'): | |
zs = np.ones(len(path)) * zs | ||
|
||
seg = [] | ||
codes = [] | ||
pathsegs = path.iter_segments(simplify=False, curves=False) | ||
for (((x, y), code), z) in zip(pathsegs, zs): | ||
seg.append((x, y, z)) | ||
codes.append(code) | ||
seg3d = [juggle_axes(x, y, z, zdir) for (x, y, z) in seg] | ||
return seg3d | ||
return seg3d, codes | ||
|
||
def paths_to_3d_segments(paths, zs=0, zdir='z'): | ||
''' | ||
|
@@ -147,9 +149,12 @@ def paths_to_3d_segments(paths, zs=0, zdir='z'): | |
zs = np.ones(len(paths)) * zs | ||
|
||
segments = [] | ||
codes_list = [] | ||
for path, pathz in zip(paths, zs): | ||
segments.append(path_to_3d_segment(path, pathz, zdir)) | ||
return segments | ||
segs, codes = path_to_3d_segment(path, pathz, zdir) | ||
segments.append(segs) | ||
codes_list.append(codes) | ||
return segments, codes_list | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same thing here. Can't change the output API without some sort of transition period. |
||
|
||
class Line3DCollection(LineCollection): | ||
''' | ||
|
@@ -196,7 +201,7 @@ def draw(self, renderer, project=False): | |
|
||
def line_collection_2d_to_3d(col, zs=0, zdir='z'): | ||
"""Convert a LineCollection to a Line3DCollection object.""" | ||
segments3d = paths_to_3d_segments(col.get_paths(), zs, zdir) | ||
segments3d = paths_to_3d_segments(col.get_paths(), zs, zdir)[0] | ||
col.__class__ = Line3DCollection | ||
col.set_segments(segments3d) | ||
|
||
|
@@ -363,6 +368,8 @@ def __init__(self, verts, *args, **kwargs): | |
|
||
PolyCollection.__init__(self, verts, *args, **kwargs) | ||
|
||
self._codes3d = None | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not quite understanding why this is needed, though. Doesn't the Polygons in the PolyCollection have their own codes? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @WeatherGod, you need to understand the existing code for my changes to make sense. Take a look at the The root cause of issue #178 is that a 2D My solution to this is to follow the existing (IMHO poor) implementation, and store the codes in As an aside, the As I said at the top of the PR, my code changes are not elegant but they are consistent (i.e. no worse than) the existing code. I didn't really want to touch any of the mplot3d code, but as you said you didn't know how to solve the problem I thought I'd give it a try. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, I see it now. A 3d polygon with a hole would need a MOVETO, but that information is never really conveyed properly. As for your aside, you are absolutely right. I am the third maintainer of this codebase, and there is a lot of stuff in it that has been "don't touch because it might break". The reason for the oddity in the object creation is probably one borne of "getting the job done". The idea for most of these things is to let the 2D plotting functions do their jobs, and then augment them with the third dimension information. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @WeatherGod: You are correct, I have not explained the fix in the code but pretty much nothing is explained in the existing mplot3d code. As I keep saying, my changes are consistent with the existing code. The more I think about it the less I want my name associated with the mplot3d codebase, hence I am closing this PR. I have diagnosed the problem and come up with a fix, albeit possibly a poor fix, so you or someone else can use my code as the basis for an acceptable fix if you wish. You still owe me a pint as for figuring about the bug as you promised in #1299! |
||
|
||
_zsort_functions = { | ||
'average': np.average, | ||
'min': np.min, | ||
|
@@ -419,6 +426,10 @@ def set_verts(self, verts, closed=True): | |
# 2D verts will be updated at draw time | ||
PolyCollection.set_verts(self, [], closed) | ||
|
||
def set_verts_and_codes(self, verts, codes): | ||
self.set_verts(verts, closed=False) | ||
self._codes3d = codes | ||
|
||
def set_3d_properties(self): | ||
# Force the collection to initialize the face and edgecolors | ||
# just in case it is a scalarmappable with a colormap. | ||
|
@@ -457,18 +468,24 @@ def do_3d_projection(self, renderer): | |
|
||
# if required sort by depth (furthest drawn first) | ||
if self._zsort: | ||
z_segments_2d = [(self._zsortfunc(zs), zip(xs, ys), fc, ec) for | ||
(xs, ys, zs), fc, ec in zip(xyzlist, cface, cedge)] | ||
indices = range(len(xyzlist)) | ||
z_segments_2d = [(self._zsortfunc(zs), zip(xs, ys), fc, ec, idx) | ||
for (xs, ys, zs), fc, ec, idx in zip(xyzlist, cface, cedge, | ||
indices)] | ||
z_segments_2d.sort(key=lambda x: x[0], reverse=True) | ||
else: | ||
raise ValueError, "whoops" | ||
|
||
segments_2d = [s for z, s, fc, ec in z_segments_2d] | ||
PolyCollection.set_verts(self, segments_2d) | ||
segments_2d = [s for z, s, fc, ec, idx in z_segments_2d] | ||
if self._codes3d is not None: | ||
codes = [self._codes3d[idx] for z, s, fc, ec, idx in z_segments_2d] | ||
PolyCollection.set_verts_and_codes(self, segments_2d, codes) | ||
else: | ||
PolyCollection.set_verts(self, segments_2d) | ||
|
||
self._facecolors2d = [fc for z, s, fc, ec in z_segments_2d] | ||
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 in z_segments_2d] | ||
self._edgecolors2d = [ec for z, s, fc, ec, idx in z_segments_2d] | ||
else: | ||
self._edgecolors2d = self._edgecolors3d | ||
|
||
|
@@ -508,9 +525,9 @@ def draw(self, renderer): | |
|
||
def poly_collection_2d_to_3d(col, zs=0, zdir='z'): | ||
"""Convert a PolyCollection to a Poly3DCollection object.""" | ||
segments_3d = paths_to_3d_segments(col.get_paths(), zs, zdir) | ||
segments_3d, codes = paths_to_3d_segments(col.get_paths(), zs, zdir) | ||
col.__class__ = Poly3DCollection | ||
col.set_verts(segments_3d) | ||
col.set_verts_and_codes(segments_3d, codes) | ||
col.set_3d_properties() | ||
|
||
def juggle_axes(xs, ys, zs, zdir): | ||
|
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.
This changes the API of this function. The correct way to go about this is to deprecate this one and have a new function set up to replace it. Or, have a kwarg defaulting to False to indicate whether to return the codes as well.
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.
@WeatherGod: I have changed all code that calls this and the other function you have highlighted. I see these as internal (i.e. private) functions, even though they are do not have leading underscores.
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.
They are not private. These are public methods to objects that people do manipulate in the wild. You will have to go through the proper process for this change.