Removed axes3ds dependency.
This commit is contained in:
@@ -11,7 +11,6 @@ from matplotlib.animation import FuncAnimation
|
||||
import matplotlib.pyplot as plt
|
||||
from scipy.spatial.transform import Rotation
|
||||
|
||||
from rotorpy.utils.axes3ds import Axes3Ds
|
||||
from rotorpy.utils.shapes import Quadrotor
|
||||
|
||||
import os
|
||||
@@ -94,12 +93,9 @@ def animate(time, position, rotation, wind, animate_wind, world, filename=None,
|
||||
else:
|
||||
fig = plt.figure('Animation')
|
||||
fig.clear()
|
||||
ax = Axes3Ds(fig)
|
||||
ax = fig.add_subplot(projection='3d')
|
||||
if not show_axes:
|
||||
ax.set_axis_off()
|
||||
ax.set_xlim(-1,1)
|
||||
ax.set_ylim(-1,1)
|
||||
ax.set_zlim(-1,1)
|
||||
|
||||
quad = Quadrotor(ax, wind=animate_wind)
|
||||
|
||||
@@ -114,7 +110,7 @@ def animate(time, position, rotation, wind, animate_wind, world, filename=None,
|
||||
def update(frame):
|
||||
title_artist.set_text('t = {:.2f}'.format(time[frame]))
|
||||
quad.transform(position=position[frame,:], rotation=rotation[frame,:,:], wind=wind[frame,:])
|
||||
[a.do_3d_projection(fig.canvas.get_renderer()) for a in quad.artists]
|
||||
# [a.do_3d_projection(fig.canvas.get_renderer()) for a in quad.artists] # No longer necessary in newer matplotlib?
|
||||
return world_artists + list(quad.artists) + [title_artist]
|
||||
|
||||
ani = ClosingFuncAnimation(fig=fig,
|
||||
|
||||
@@ -1,159 +0,0 @@
|
||||
"""
|
||||
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
|
||||
@@ -233,7 +233,6 @@ class OccupancyMap:
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
from axes3ds import Axes3Ds
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
import os
|
||||
@@ -245,7 +244,7 @@ if __name__ == "__main__":
|
||||
|
||||
# Create a figure
|
||||
fig = plt.figure()
|
||||
ax = Axes3Ds(fig)
|
||||
ax = fig.add_subplot(projection='3d')
|
||||
# Draw the world
|
||||
world.draw(ax)
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import numpy as np
|
||||
from scipy.spatial.transform import Rotation
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from rotorpy.utils.axes3ds import Axes3Ds
|
||||
# from rotorpy.utils.axes3ds import Axes3Ds
|
||||
from rotorpy.utils.animate import animate
|
||||
|
||||
import os
|
||||
@@ -38,7 +38,8 @@ class Plotter():
|
||||
|
||||
# 3D Paths
|
||||
fig = plt.figure('3D Path')
|
||||
ax = Axes3Ds(fig)
|
||||
# ax = Axes3Ds(fig)
|
||||
ax = fig.add_subplot(projection='3d')
|
||||
self.world.draw(ax)
|
||||
ax.plot3D(self.x[:,0], self.x[:,1], self.x[:,2], 'b.')
|
||||
ax.plot3D(self.x_des[:,0], self.x_des[:,1], self.x_des[:,2], 'k')
|
||||
|
||||
@@ -14,6 +14,58 @@ from mpl_toolkits.mplot3d import art3d
|
||||
import matplotlib.colors as mcolors
|
||||
from scipy.spatial.transform import Rotation
|
||||
|
||||
"""
|
||||
Necessary functions for visualization
|
||||
From original mplot3d version by John Porter (Created: 23 Sep 2005)
|
||||
|
||||
Parts fixed by Reinier Heeres <reinier@heeres.eu>
|
||||
Minor additions by Ben Axelrod <baxelrod@coroware.com>
|
||||
Significant updates and revisions by Ben Root <ben.v.root@gmail.com>
|
||||
|
||||
Current as of matplotlib v3.2.2 but changed at some point.
|
||||
Modified by Spencer Folk
|
||||
"""
|
||||
|
||||
def _generate_normals(polygons):
|
||||
'''
|
||||
Generate normals for polygons by using the first three points.
|
||||
This normal of course might not make sense for polygons with
|
||||
more than three points not lying in a plane.
|
||||
'''
|
||||
|
||||
normals = []
|
||||
for verts in polygons:
|
||||
v1 = np.array(verts[0]) - np.array(verts[1])
|
||||
v2 = np.array(verts[2]) - np.array(verts[0])
|
||||
normals.append(np.cross(v1, v2))
|
||||
return normals
|
||||
|
||||
def _shade_colors(color, normals):
|
||||
'''
|
||||
Shade *color* using normal vectors given by *normals*.
|
||||
*color* can also be an array of the same length as *normals*.
|
||||
'''
|
||||
|
||||
shade = np.array([np.dot(n / np.linalg.norm(n), [-1, -1, 0.5])
|
||||
if np.linalg.norm(n) else np.nan
|
||||
for n in normals])
|
||||
mask = ~np.isnan(shade)
|
||||
|
||||
if len(shade[mask]) > 0:
|
||||
norm = mcolors.Normalize(min(shade[mask]), max(shade[mask]))
|
||||
shade[~mask] = min(shade[mask])
|
||||
color = mcolors.to_rgba_array(color)
|
||||
# shape of color should be (M, 4) (where M is number of faces)
|
||||
# shape of shade should be (M,)
|
||||
# colors should have final shape of (M, 4)
|
||||
alpha = color[:, 3]
|
||||
colors = (0.5 + norm(shade)[:, np.newaxis] * 0.5) * color
|
||||
colors[:, 3] = alpha
|
||||
else:
|
||||
colors = np.asanyarray(color).copy()
|
||||
|
||||
return colors
|
||||
|
||||
class Face():
|
||||
|
||||
def __init__(self, ax, corners, *,
|
||||
@@ -45,7 +97,7 @@ class Face():
|
||||
|
||||
# Precompute verticies and normal vectors in reference configuration.
|
||||
self.verts = np.reshape(corners, (1, -1, 3))
|
||||
self.normals = np.asarray(self.ax._generate_normals(self.verts))
|
||||
self.normals = np.asarray(_generate_normals(self.verts))
|
||||
|
||||
# Instantiate and add collection.
|
||||
self.polyc = art3d.Poly3DCollection(self.verts, linewidth=linewidth, antialiased=antialiased, alpha=alpha, edgecolors=edgecolors, facecolors=self.facecolors)
|
||||
@@ -65,7 +117,7 @@ class Face():
|
||||
|
||||
if self.shade:
|
||||
normals = np.matmul(rotation, self.normals.T).T
|
||||
colset = self.ax._shade_colors(self.facecolors, normals)
|
||||
colset = _shade_colors(self.facecolors, normals)
|
||||
else:
|
||||
colset = self.facecolors
|
||||
self.polyc.set_facecolors(colset)
|
||||
@@ -103,7 +155,7 @@ class Cuboid():
|
||||
|
||||
# Precompute verticies and normal vectors in reference configuration.
|
||||
self.verts = self.build_verts(x_span, y_span, z_span)
|
||||
self.normals = np.asarray(self.ax._generate_normals(self.verts))
|
||||
self.normals = np.asarray(_generate_normals(self.verts))
|
||||
|
||||
# Instantiate and add collection.
|
||||
self.polyc = art3d.Poly3DCollection(self.verts, linewidth=linewidth, antialiased=antialiased, alpha=alpha, edgecolors=edgecolors, facecolors=self.facecolors)
|
||||
@@ -123,7 +175,7 @@ class Cuboid():
|
||||
|
||||
if self.shade:
|
||||
normals = np.matmul(rotation, self.normals.T).T
|
||||
colset = self.ax._shade_colors(self.facecolors, normals)
|
||||
colset = _shade_colors(self.facecolors, normals)
|
||||
else:
|
||||
colset = self.facecolors
|
||||
self.polyc.set_facecolors(colset)
|
||||
@@ -181,7 +233,7 @@ class Cylinder():
|
||||
|
||||
# Precompute verticies and normal vectors in reference configuration.
|
||||
self.verts = self.build_verts(radius, height, n_pts)
|
||||
self.normals = np.asarray(self.ax._generate_normals(self.verts))
|
||||
self.normals = np.asarray(_generate_normals(self.verts))
|
||||
|
||||
# Instantiate and add collection.
|
||||
self.polyc = art3d.Poly3DCollection(self.verts, color='b', linewidth=0, antialiased=False)
|
||||
@@ -200,7 +252,7 @@ class Cylinder():
|
||||
|
||||
if self.shade:
|
||||
normals = np.matmul(rotation, self.normals.T).T
|
||||
colset = self.ax._shade_colors(self.color, normals)
|
||||
colset = _shade_colors(self.color, normals)
|
||||
else:
|
||||
colset = self.color
|
||||
self.polyc.set_facecolors(colset)
|
||||
@@ -311,12 +363,11 @@ class Quadrotor():
|
||||
self.wind_vector = [self.ax.quiver(position[0], position[1], position[2], wind[0], wind[1], wind[2], color='r', linewidth=1.5)]
|
||||
|
||||
if __name__ == '__main__':
|
||||
from axes3ds import Axes3Ds
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
# Test Face
|
||||
fig = plt.figure(num=4, clear=True)
|
||||
ax = Axes3Ds(fig)
|
||||
ax = fig.add_subplot(projection='3d')
|
||||
corners = np.array([(1,1,1), (-1,1,1), (-1,-1,1), (1,-1,1)])
|
||||
z_plus_face = Face(ax, corners=corners, facecolors='b')
|
||||
x_plus_face = Face(ax, corners=corners, facecolors='r')
|
||||
@@ -336,7 +387,7 @@ if __name__ == '__main__':
|
||||
|
||||
# Test Cuboid
|
||||
fig = plt.figure(num=0, clear=True)
|
||||
ax = Axes3Ds(fig)
|
||||
ax = fig.add_subplot(projection='3d')
|
||||
cuboid = Cuboid(ax, x_span=1, y_span=1, z_span=1)
|
||||
cuboid.transform(position=np.array([[0, 0, 0]]), rotation=np.identity(3))
|
||||
rotation = Rotation.from_rotvec(np.pi/4 * np.array([1, 0, 0])).as_matrix()
|
||||
@@ -348,7 +399,7 @@ if __name__ == '__main__':
|
||||
|
||||
# Test Cylinder
|
||||
fig = plt.figure(num=1, clear=True)
|
||||
ax = Axes3Ds(fig)
|
||||
ax = fig.add_subplot(projection='3d')
|
||||
cylinder = Cylinder(ax, radius=0.2, height=0.2)
|
||||
cylinder.transform(position=np.array([[0, 0, 0]]), rotation=np.identity(3))
|
||||
rotation = Rotation.from_rotvec(np.pi/4 * np.array([1, 0, 0])).as_matrix()
|
||||
@@ -360,7 +411,7 @@ if __name__ == '__main__':
|
||||
|
||||
# Test Quadrotor
|
||||
fig = plt.figure(num=2, clear=True)
|
||||
ax = Axes3Ds(fig)
|
||||
ax = fig.add_subplot(projection='3d')
|
||||
quad = Quadrotor(ax)
|
||||
quad.transform(position=np.array([[0.5, 0.5, 0.5]]), rotation=np.identity(3))
|
||||
quad = Quadrotor(ax)
|
||||
@@ -372,7 +423,7 @@ if __name__ == '__main__':
|
||||
|
||||
# Test Cuboid coloring.
|
||||
fig = plt.figure(num=3, clear=True)
|
||||
ax = Axes3Ds(fig)
|
||||
ax = fig.add_subplot(projection='3d')
|
||||
ax.set_xlim(-3.25,3.25)
|
||||
ax.set_ylim(-3.25,3.25)
|
||||
ax.set_zlim(-3.25,3.25)
|
||||
|
||||
@@ -289,7 +289,6 @@ if __name__ == '__main__':
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
import matplotlib.pyplot as plt
|
||||
from rotorpy.axes3ds import Axes3Ds
|
||||
|
||||
parser = argparse.ArgumentParser(description='Display a map file in a Matplotlib window.')
|
||||
parser.add_argument('filename', help="Filename for map file json.")
|
||||
@@ -299,7 +298,7 @@ if __name__ == '__main__':
|
||||
world = World.from_file(file)
|
||||
|
||||
fig = plt.figure(f"{file.name}")
|
||||
ax = Axes3Ds(fig)
|
||||
ax = fig.add_subplot(projection='3d')
|
||||
world.draw(ax)
|
||||
|
||||
plt.show()
|
||||
|
||||
4
setup.py
4
setup.py
@@ -3,7 +3,7 @@ from os.path import isdir
|
||||
from itertools import product
|
||||
|
||||
# Gather our flightsim and any projXX packages that happen to exist.
|
||||
all_packages = ['rotorpy', 'rotorpy.wind-dynamics']
|
||||
all_packages = ['rotorpy']
|
||||
packages = list(filter(isdir, all_packages))
|
||||
|
||||
setup(
|
||||
@@ -12,7 +12,7 @@ setup(
|
||||
version='1.0.1',
|
||||
install_requires=[
|
||||
'cvxopt',
|
||||
'matplotlib == 3.2.2',
|
||||
'matplotlib',
|
||||
'filterpy == 1.4.5',
|
||||
'numpy',
|
||||
'scipy',
|
||||
|
||||
Reference in New Issue
Block a user