160 lines
6.1 KiB
Python
160 lines
6.1 KiB
Python
"""
|
|
This module provides Axes3Ds ("Axes3D Spatial"), a drop-in replacement for
|
|
Axes3D which incorporates the improvements proposed by eric-wieser in matplotlib
|
|
issue #8896.
|
|
|
|
The purpose is to reduce the distortion when projecting 3D scenes into the 2D
|
|
image. For example, the projection of a sphere will be (closer to) a circle.
|
|
"""
|
|
|
|
"""
|
|
License agreement for matplotlib versions 1.3.0 and later
|
|
=========================================================
|
|
|
|
1. This LICENSE AGREEMENT is between the Matplotlib Development Team
|
|
("MDT"), and the Individual or Organization ("Licensee") accessing and
|
|
otherwise using matplotlib software in source or binary form and its
|
|
associated documentation.
|
|
|
|
2. Subject to the terms and conditions of this License Agreement, MDT
|
|
hereby grants Licensee a nonexclusive, royalty-free, world-wide license
|
|
to reproduce, analyze, test, perform and/or display publicly, prepare
|
|
derivative works, distribute, and otherwise use matplotlib
|
|
alone or in any derivative version, provided, however, that MDT's
|
|
License Agreement and MDT's notice of copyright, i.e., "Copyright (c)
|
|
2012- Matplotlib Development Team; All Rights Reserved" are retained in
|
|
matplotlib alone or in any derivative version prepared by
|
|
Licensee.
|
|
|
|
3. In the event Licensee prepares a derivative work that is based on or
|
|
incorporates matplotlib or any part thereof, and wants to
|
|
make the derivative work available to others as provided herein, then
|
|
Licensee hereby agrees to include in any such work a brief summary of
|
|
the changes made to matplotlib .
|
|
|
|
4. MDT is making matplotlib available to Licensee on an "AS
|
|
IS" basis. MDT MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
|
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, MDT MAKES NO AND
|
|
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
|
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF MATPLOTLIB
|
|
WILL NOT INFRINGE ANY THIRD PARTY RIGHTS.
|
|
|
|
5. MDT SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF MATPLOTLIB
|
|
FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR
|
|
LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING
|
|
MATPLOTLIB , OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF
|
|
THE POSSIBILITY THEREOF.
|
|
|
|
6. This License Agreement will automatically terminate upon a material
|
|
breach of its terms and conditions.
|
|
|
|
7. Nothing in this License Agreement shall be deemed to create any
|
|
relationship of agency, partnership, or joint venture between MDT and
|
|
Licensee. This License Agreement does not grant permission to use MDT
|
|
trademarks or trade name in a trademark sense to endorse or promote
|
|
products or services of Licensee, or any third party.
|
|
|
|
8. By copying, installing or otherwise using matplotlib ,
|
|
Licensee agrees to be bound by the terms and conditions of this License
|
|
Agreement.
|
|
|
|
"""
|
|
|
|
import numpy as np
|
|
|
|
from mpl_toolkits.mplot3d import Axes3D
|
|
from mpl_toolkits.mplot3d import proj3d
|
|
|
|
# Patch note: An update of the original implementation in proj3d.py.
|
|
def world_transformation(xmin, xmax,
|
|
ymin, ymax,
|
|
zmin, zmax, pb_aspect=None):
|
|
"""
|
|
produce a matrix that scales homogenous coords in the specified ranges
|
|
to [0, 1], or [0, pb_aspect[i]] if the plotbox aspect ratio is specified
|
|
"""
|
|
dx = xmax - xmin
|
|
dy = ymax - ymin
|
|
dz = zmax - zmin
|
|
if pb_aspect is not None:
|
|
ax, ay, az = pb_aspect
|
|
dx /= ax
|
|
dy /= ay
|
|
dz /= az
|
|
|
|
return np.array([[1/dx, 0, 0, -xmin/dx],
|
|
[0, 1/dy, 0, -ymin/dy],
|
|
[0, 0, 1/dz, -zmin/dz],
|
|
[0, 0, 0, 1]])
|
|
|
|
class Axes3Ds(Axes3D):
|
|
"""
|
|
Class Axes3Ds ("Axes3D Spatial") is a drop-in replacement for Axes3D which
|
|
incorporates the improvements proposed by eric-wieser in matplotlib issue
|
|
#8896.
|
|
"""
|
|
|
|
# Patch note: A new function.
|
|
def apply_aspect(self, position=None):
|
|
if position is None:
|
|
position = self.get_position(original=True)
|
|
|
|
# in the superclass, we would go through and actually deal with axis
|
|
# scales and box/datalim. Those are all irrelevant - all we need to do
|
|
# is make sure our coordinate system is square.
|
|
figW, figH = self.get_figure().get_size_inches()
|
|
fig_aspect = figH / figW
|
|
box_aspect = 1
|
|
pb = position.frozen()
|
|
pb1 = pb.shrunk_to_aspect(box_aspect, pb, fig_aspect)
|
|
self.set_position(pb1.anchored(self.get_anchor(), pb), 'active')
|
|
|
|
# Patch note: Overwritten to use the updated version of world_transformation
|
|
# and the new pb_aspect value.
|
|
def get_proj(self):
|
|
"""
|
|
Create the projection matrix from the current viewing position.
|
|
elev stores the elevation angle in the z plane
|
|
azim stores the azimuth angle in the x,y plane
|
|
dist is the distance of the eye viewing point from the object
|
|
point.
|
|
"""
|
|
# chosen for similarity with the initial view before gh-8896
|
|
pb_aspect = np.array([4, 4, 3]) / 3.5
|
|
|
|
relev, razim = np.pi * self.elev/180, np.pi * self.azim/180
|
|
|
|
xmin, xmax = self.get_xlim3d()
|
|
ymin, ymax = self.get_ylim3d()
|
|
zmin, zmax = self.get_zlim3d()
|
|
|
|
# transform to uniform world coordinates 0-1.0,0-1.0,0-1.0
|
|
worldM = world_transformation(xmin, xmax,
|
|
ymin, ymax,
|
|
zmin, zmax, pb_aspect=pb_aspect)
|
|
|
|
# look into the middle of the new coordinates
|
|
R = pb_aspect / 2
|
|
|
|
xp = R[0] + np.cos(razim) * np.cos(relev) * self.dist
|
|
yp = R[1] + np.sin(razim) * np.cos(relev) * self.dist
|
|
zp = R[2] + np.sin(relev) * self.dist
|
|
E = np.array((xp, yp, zp))
|
|
|
|
self.eye = E
|
|
self.vvec = R - E
|
|
self.vvec = self.vvec / np.linalg.norm(self.vvec)
|
|
|
|
if abs(relev) > np.pi/2:
|
|
# upside down
|
|
V = np.array((0, 0, -1))
|
|
else:
|
|
V = np.array((0, 0, 1))
|
|
zfront, zback = -self.dist, self.dist
|
|
|
|
viewM = proj3d.view_transformation(E, R, V)
|
|
projM = self._projection(zfront, zback)
|
|
M0 = np.dot(viewM, worldM)
|
|
M = np.dot(projM, M0)
|
|
return M
|