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

Skip to content

Orthographic projection for mplot3d #8326

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

Merged
merged 7 commits into from
Apr 10, 2017

Conversation

txxia
Copy link
Contributor

@txxia txxia commented Mar 18, 2017

Fixes #537

  • implemented ortho_transformation() in proj3d
  • added a flag persp and a setter set_persp()

Here is a simple demo:

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import proj3d
fig = plt.figure()
def init_axes(ax):
    ax.set_aspect("equal")
    ax.set_xlabel('x')
    ax.set_ylabel('y')
    ax.set_zlabel('z')
ax = fig.add_subplot(121, projection='3d', persp=False)
init_axes(ax)
ax = fig.add_subplot(122, projection='3d', persp=True)
init_axes(ax)
plt.show()

figure_1

@dstansby dstansby added this to the 2.1 (next point release) milestone Mar 18, 2017
Copy link
Member

@dstansby dstansby left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Neat little new feature! Just a couple of implementation changes I've suggested.

@@ -63,6 +63,7 @@ def __init__(self, fig, rect=None, *args, **kwargs):
*elev* Elevation viewing angle (default 30)
*zscale* [%(scale)s]
*sharez* Other axes to share z-limits with
*persp* Perspective projection (default True)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be better to have a proj argument instead, which (at the moment) could take either 'persp or ortho values, and default to persp.

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 agree. In that case, should I provide the setter set_proj? There is a get_proj which has different meaning.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe use projection instead of proj to avoid breaking get_proj?

I think a better alternative would be to rename the current get_proj to get_proj_matrix - might be worth seeing what other devs think about that.

fig = plt.figure()
ax = fig.gca(projection='3d')
ax.set_persp(False)
ax.set_xlabel('X')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test doesn't really need any text, so could the label lines be removed.

- renamed persp to proj
- renamed get_proj to get_proj_matrix
- add test for ortho_projection
@txxia
Copy link
Contributor Author

txxia commented Mar 20, 2017

I am not too sure about this. The name projection conflicts with the flag in add_subplot (see above demo), but renaming get_proj may break users' code if they happen to be using it.

# eye point
E = np.array([1000, -1000, 2000])
R = np.array([100, 100, 100])
V = np.array([0, 0, 1])
viewM = proj3d.view_transformation(E, R, V)
perspM = proj3d.persp_transformation(100, -100)
Copy link
Contributor Author

@txxia txxia Mar 20, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure about this either. This seems to assume zfront and zback refer to the coordinates on the camera's Z axis (which is backwards), but judging from other places where it's called, they seem to be equivalent to near / far plane.
https://github.com/matplotlib/matplotlib/blob/master/lib/mpl_toolkits/mplot3d/axes3d.py#L1001

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is entirely possible this test was originally backwards. It came from some commented out code at the bottom of proj3d.py. Seemed like it might have been some debugging code for the original developer. We recently lifted it out of there and put it in as a unit test because it helped us solve a problem.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, thanks for the clarification. I will leave it as is then.

@WeatherGod
Copy link
Member

it is unfortunate name choice, but get_proj() has been a part of the public API for a very long time now. I do know of code in the wild that uses it. Please do not change that.

def get_proj(self):
def set_proj(self, proj):
"""
Set the type of projection.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to expand on this; detail what are the accepted inputs and what they mean.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think it will be okay if I rename the flag and this setter to proj_type? since I will keep get_proj, using set_proj would cause confusion.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think your new setter/getter can be proj_type. I am a little leery of using the word "type" in a variable name when one is not referring to a type object, but I think it is ok here, especially if it is documented well.

# eye point
E = np.array([1000, -1000, 2000])
R = np.array([100, 100, 100])
V = np.array([0, 0, 1])
viewM = proj3d.view_transformation(E, R, V)
perspM = proj3d.persp_transformation(100, -100)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is entirely possible this test was originally backwards. It came from some commented out code at the bottom of proj3d.py. Seemed like it might have been some debugging code for the original developer. We recently lifted it out of there and put it in as a unit test because it helped us solve a problem.

[0,2,0,0],
[0,0,-2,0],
[0,0,a,b]
])
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 provide a reference for this formulation? I am not an expert in 3d transforms, but this doesn't match anything I am seeing on the wikipedia page for orthographic projection.

Copy link
Contributor Author

@txxia txxia Mar 29, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I should have justified it.
This matrix is almost equivalent to the one in the Wikipedia page. Based on persp_transformation, I made these assumptions:

  • zfront is the value for near, left and bottom
  • zback for far, right, top
  • the X and Y translations are omitted (I guess so the image will always be centered at the origin)

It's a simple derivation from the one from Wikipedia (v1), but since v2 and v1 are equivalent in homogeneous coordinates, I thought the second will look nicer.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, and if I transpose that, it then matches up. Got it. Well, just include some notes about it in comments for some future poor soul.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this the transpose?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because numpy matrix is in row major order?

@@ -961,9 +961,15 @@ def view_init(self, elev=None, azim=None):
else:
self.azim = azim

def set_proj(self, proj):
def set_proj_type(self, proj):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why didn't you rename the argument name as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing out, I just renamed the argument.

@WeatherGod
Copy link
Member

This looks great. The only thing left to do is to add an entry to the what's new page: There should be some notes in doc/users/whats_new/

@txxia
Copy link
Contributor Author

txxia commented Apr 4, 2017

I am new to numpydoc. The file I created seems to compile, but I had trouble generating everything under doc/ (it gave KeyError: b'tcsi1728' at some point). Should I worry about that?

@WeatherGod
Copy link
Member

we build the docs as part of our test suite. If it builds there, that should be sufficient.

@WeatherGod
Copy link
Member

It would be nice to get more documentation for this feature, especially prose and graphics showing when this feature makes a significant improvement. However, that can be done in a separate PR.

@WeatherGod
Copy link
Member

@dstansby , I take it that your concerns have been addressed?

@dstansby
Copy link
Member

dstansby commented Apr 9, 2017 via email

@WeatherGod WeatherGod merged commit 6f4cc13 into matplotlib:master Apr 10, 2017
@WeatherGod
Copy link
Member

Thanks to everybody!

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.

5 participants