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

Skip to content

Commit a3bdaf8

Browse files
Reach vectorized projection functions directly
1 parent 9c13619 commit a3bdaf8

File tree

3 files changed

+77
-84
lines changed

3 files changed

+77
-84
lines changed

lib/mpl_toolkits/mplot3d/art3d.py

Lines changed: 72 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -115,14 +115,10 @@ def set_3d_properties(self, zs=0, zdir='z'):
115115
xs = self.get_xdata()
116116
ys = self.get_ydata()
117117

118-
try:
119-
# If *zs* is a list or array, then this will fail and
120-
# just proceed to juggle_axes().
121-
zs = float(zs)
122-
zs = [zs for x in xs]
123-
except TypeError:
124-
pass
125-
self._verts3d = juggle_axes(xs, ys, zs, zdir)
118+
if not iterable(zs):
119+
zs = np.ones(len(xs)) * zs
120+
xyz = np.asarray([xs, ys, zs])
121+
self._verts3d = juggle_axes(xyz, zdir)
126122
self.stale = True
127123

128124
def draw(self, renderer):
@@ -143,15 +139,17 @@ def line_2d_to_3d(line, zs=0, zdir='z'):
143139
def path_to_3d_segment(path, zs=0, zdir='z'):
144140
'''Convert a path to a 3D segment.'''
145141

146-
if not iterable(zs):
147-
zs = np.ones(len(path)) * zs
142+
# Pre allocate memory
143+
seg3d = np.ones((3, len(path)))
148144

149-
seg = []
145+
# Works either if zs is array or scalar
146+
seg3d[2] *= zs
147+
150148
pathsegs = path.iter_segments(simplify=False, curves=False)
151-
for (((x, y), code), z) in zip(pathsegs, zs):
152-
seg.append((x, y, z))
153-
seg3d = [juggle_axes(x, y, z, zdir) for (x, y, z) in seg]
154-
return seg3d
149+
for i, ((x, y), code) in enumerate(pathsegs):
150+
seg3d[0:2, i] = x, y
151+
seg3d = juggle_axes(seg3d, zdir)
152+
return seg3d.T
155153

156154
def paths_to_3d_segments(paths, zs=0, zdir='z'):
157155
'''
@@ -164,22 +162,24 @@ def paths_to_3d_segments(paths, zs=0, zdir='z'):
164162
segments = []
165163
for path, pathz in zip(paths, zs):
166164
segments.append(path_to_3d_segment(path, pathz, zdir))
167-
return segments
165+
return np.asarray(segments)
168166

169167
def path_to_3d_segment_with_codes(path, zs=0, zdir='z'):
170168
'''Convert a path to a 3D segment with path codes.'''
169+
# Pre allocate memory
170+
# XXX should we consider a 4d array?
171+
seg3d = np.ones((3, len(path)))
171172

172-
if not iterable(zs):
173-
zs = np.ones(len(path)) * zs
173+
# Works either if zs is array or scalar
174+
seg3d[2] *= zs
174175

175-
seg = []
176-
codes = []
177176
pathsegs = path.iter_segments(simplify=False, curves=False)
178-
for (((x, y), code), z) in zip(pathsegs, zs):
179-
seg.append((x, y, z))
180-
codes.append(code)
181-
seg3d = [juggle_axes(x, y, z, zdir) for (x, y, z) in seg]
182-
return seg3d, codes
177+
codes = np.empty(len(path))
178+
for i, ((x, y), code) in enumerate(pathsegs):
179+
seg3d[0:2, i] = x, y
180+
codes[i] = code
181+
seg3d = juggle_axes(seg3d, zdir)
182+
return seg3d.T, codes
183183

184184
def paths_to_3d_segments_with_codes(paths, zs=0, zdir='z'):
185185
'''
@@ -195,7 +195,7 @@ def paths_to_3d_segments_with_codes(paths, zs=0, zdir='z'):
195195
segs, codes = path_to_3d_segment_with_codes(path, pathz, zdir)
196196
segments.append(segs)
197197
codes_list.append(codes)
198-
return segments, codes_list
198+
return np.asarray(segments), np.asarray(codes_list)
199199

200200
class Line3DCollection(LineCollection):
201201
'''
@@ -224,10 +224,16 @@ def do_3d_projection(self, renderer):
224224
'''
225225
Project the points according to renderer matrix.
226226
'''
227-
xys = proj3d.proj_trans_points(self._segments3d, renderer.M)
228-
segments_2d = xys[0:1, :]
227+
shape = self._segments3d.shape
228+
segments3d = np.transpose(self._segments3d, axes=[2, 0, 1])
229+
segments3d = np.lib.pad(
230+
segments3d, ((0, 1), (0, 0), (0, 0)),
231+
'constant', constant_values=1)
232+
xys = proj3d.proj_transform_vec(segments3d.reshape(4, -1), renderer.M)
233+
xys = np.reshape(xys.T, shape)
234+
segments_2d = xys[:, :, 0:2]
229235
LineCollection.set_segments(self, segments_2d)
230-
minz = np.min(xys[2, :])
236+
minz = np.min(xys[:, :, 2])
231237
return minz
232238

233239
def draw(self, renderer, project=False):
@@ -255,11 +261,8 @@ def __init__(self, *args, **kwargs):
255261
self.set_3d_properties(zs, zdir)
256262

257263
def set_3d_properties(self, verts, zs=0, zdir='z'):
258-
if not iterable(zs):
259-
zs = np.ones(len(verts)) * zs
260-
261-
self._segment3d = [juggle_axes(x, y, z, zdir) \
262-
for ((x, y), z) in zip(verts, zs)]
264+
verts = np.vstack(verts, np.ones(len(verts)) * zs)
265+
self._segment3d = juggle_axes(verts, zdir)
263266
self._facecolor3d = Patch.get_facecolor(self)
264267

265268
def get_path(self):
@@ -269,9 +272,9 @@ def get_facecolor(self):
269272
return self._facecolor2d
270273

271274
def do_3d_projection(self, renderer):
272-
s = self._segment3d
273-
xs, ys, zs = list(zip(*s))
274-
vxyzis = proj3d.proj_transform_clip(xs, ys, zs, renderer.M)
275+
# pad ones
276+
s = np.vstack(self._segment3d, np.ones(self._segment3d.shape[1]))
277+
vxyzis = proj3d.proj_transform_vec_clip(s, renderer.M)
275278
self._path2d = mpath.Path(vxyzis[0:2].T)
276279
# FIXME: coloring
277280
self._facecolor2d = self._facecolor3d
@@ -297,9 +300,9 @@ def set_3d_properties(self, path, zs=0, zdir='z'):
297300
self._code3d = path.codes
298301

299302
def do_3d_projection(self, renderer):
300-
s = self._segment3d
301-
xs, ys, zs = list(zip(*s))
302-
vxyzis = proj3d.proj_transform_clip(xs, ys, zs, renderer.M)
303+
# pad ones
304+
s = np.vstack(self._segmenta3d, np.ones(self._segment3d.shape[1]))
305+
vxyzis = proj3d.proj_transform_vec_clip(s, renderer.M)
303306
self._path2d = mpath.Path(vxyzis[0:2].T, self._code3d)
304307
# FIXME: coloring
305308
self._facecolor2d = self._facecolor3d
@@ -366,20 +369,16 @@ def set_3d_properties(self, zs, zdir):
366369
# Force the collection to initialize the face and edgecolors
367370
# just in case it is a scalarmappable with a colormap.
368371
self.update_scalarmappable()
369-
offsets = self.get_offsets()
370-
if len(offsets) > 0:
371-
xs, ys = list(zip(*offsets))
372-
else:
373-
xs = []
374-
ys = []
375-
self._offsets3d = juggle_axes(xs, ys, np.atleast_1d(zs), zdir)
372+
offsets = np.vstack(self.get_offsets(), np.ones(len(verts)) * zs)
373+
self._offsets3d = juggle_axes(offsets, zdir)
376374
self._facecolor3d = self.get_facecolor()
377375
self._edgecolor3d = self.get_edgecolor()
378376
self.stale = True
379377

380378
def do_3d_projection(self, renderer):
381-
xs, ys, zs = self._offsets3d
382-
vxyzis = proj3d.proj_transform_clip(xs, ys, zs, renderer.M)
379+
# pad ones
380+
s = np.vstack(self._offsets3d, np.ones(self._offsets3d.shape[1]))
381+
vxyzis = proj3d.proj_transform_vec_clip(s, renderer.M)
383382

384383
fcs = (zalpha(self._facecolor3d, vxyzis[2]) if self._depthshade else
385384
self._facecolor3d)
@@ -434,13 +433,8 @@ def set_3d_properties(self, zs, zdir):
434433
# Force the collection to initialize the face and edgecolors
435434
# just in case it is a scalarmappable with a colormap.
436435
self.update_scalarmappable()
437-
offsets = self.get_offsets()
438-
if len(offsets) > 0:
439-
xs, ys = list(zip(*offsets))
440-
else:
441-
xs = []
442-
ys = []
443-
self._offsets3d = juggle_axes(xs, ys, np.atleast_1d(zs), zdir)
436+
offsets = np.vstack(self.get_offsets(), np.ones(len(verts)) * zs)
437+
self._offsets3d = juggle_axes(offsets, zdir)
444438
self._facecolor3d = self.get_facecolor()
445439
self._edgecolor3d = self.get_edgecolor()
446440
self.stale = True
@@ -545,12 +539,13 @@ def set_zsort(self, zsort):
545539

546540
def get_vector(self, segments3d):
547541
"""Optimize points for projection"""
548-
# Segments 3d are given in shape (n_segments, 3, 3)
549-
# Flatten them
550-
xys = segments3d.reshape((-1, 3)).T
542+
# Segments 3d are given in shape (n_segments, segsize, 3)
543+
self._segsize = segments3d.shape[1]
544+
# Flatten
545+
xyz = segments3d.T.reshape((3, -1))
551546
# Add a fourth dimension with only ones
552-
ones = np.ones(xys.shape[1])
553-
self._vec = np.vstack([xys, ones])
547+
ones = np.ones(xyz.shape[1])
548+
self._vec = np.vstack([xyz, ones])
554549

555550
def set_verts(self, verts, closed=True):
556551
'''Set 3D vertices.'''
@@ -593,8 +588,9 @@ def do_3d_projection(self, renderer):
593588
self._facecolors3d = self._facecolors
594589

595590
xys = proj3d.proj_transform_vec(self._vec, renderer.M)
596-
xyzlist = np.transpose(xys.T.reshape((-1, 3, 3)), axes=[0, 2, 1])
597-
591+
xys = np.reshape(xys, (3, self._segsize, -1))
592+
xyzlist = xys.T
593+
598594
# This extra fuss is to re-order face / edge colors
599595
cface = self._facecolors3d
600596
cedge = self._edgecolors3d
@@ -608,11 +604,11 @@ def do_3d_projection(self, renderer):
608604
# if required sort by depth (furthest drawn first)
609605
if self._zsort:
610606
z_argsort = np.argsort(
611-
self._zsortfunc(xyzlist[:, 2, :], axis=1))[::-1]
607+
self._zsortfunc(xyzlist[:, :, 2], axis=1))[::-1]
612608
else:
613609
raise ValueError("whoops")
614610

615-
segments_2d = np.transpose(xyzlist[z_argsort, 0:2, :], axes=[0, 2, 1])
611+
segments_2d = xyzlist[z_argsort, :, 0:2]
616612
if self._codes3d is not None:
617613
codes = self._codes3d[z_argsort]
618614
PolyCollection.set_verts_and_codes(self, segments_2d, codes)
@@ -693,39 +689,39 @@ def poly_collection_2d_to_3d(col, zs=0, zdir='z'):
693689
col.set_verts_and_codes(segments_3d, codes)
694690
col.set_3d_properties()
695691

696-
def juggle_axes(xs, ys, zs, zdir):
692+
def juggle_axes(xyz, zdir):
697693
"""
698694
Reorder coordinates so that 2D xs, ys can be plotted in the plane
699695
orthogonal to zdir. zdir is normally x, y or z. However, if zdir
700696
starts with a '-' it is interpreted as a compensation for rotate_axes.
701697
"""
702698
if zdir == 'x':
703-
return zs, xs, ys
699+
return xyz[[2, 0, 1]]
704700
elif zdir == 'y':
705-
return xs, zs, ys
701+
return xyz[[0, 2, 1]]
706702
elif zdir[0] == '-':
707-
return rotate_axes(xs, ys, zs, zdir)
703+
return rotate_axes(xyz, zdir)
708704
else:
709-
return xs, ys, zs
705+
return xyz
710706

711-
def rotate_axes(xs, ys, zs, zdir):
707+
def rotate_axes(xyz, zdir):
712708
"""
713709
Reorder coordinates so that the axes are rotated with zdir along
714710
the original z axis. Prepending the axis with a '-' does the
715711
inverse transform, so zdir can be x, -x, y, -y, z or -z
716712
"""
717713
if zdir == 'x':
718-
return ys, zs, xs
714+
return xyz[[1, 2, 0]]
719715
elif zdir == '-x':
720-
return zs, xs, ys
716+
return xyz[[2, 0, 1]]
721717

722718
elif zdir == 'y':
723-
return zs, xs, ys
719+
return xyz[[2, 0, 1]]
724720
elif zdir == '-y':
725-
return ys, zs, xs
721+
return xyz[[1, 2, 0]]
726722

727723
else:
728-
return xs, ys, zs
724+
return xyz
729725

730726
def iscolor(c):
731727
try:

lib/mpl_toolkits/mplot3d/axes3d.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2329,15 +2329,9 @@ def bar(self, left, height, zs=0, zdir='z', *args, **kwargs):
23292329
if 'alpha' in kwargs:
23302330
p.set_alpha(kwargs['alpha'])
23312331

2332-
if len(verts) > 0 :
2333-
# the following has to be skipped if verts is empty
2334-
# NOTE: Bugs could still occur if len(verts) > 0,
2335-
# but the "2nd dimension" is empty.
2336-
xs, ys = list(zip(*verts))
2337-
else :
2338-
xs, ys = [], []
2332+
verts = np.vstack(verts, verts_zs)
23392333

2340-
xs, ys, verts_zs = art3d.juggle_axes(xs, ys, verts_zs, zdir)
2334+
xs, ys, verts_zs = art3d.juggle_axes(verts, zdir)
23412335
self.auto_scale_xyz(xs, ys, verts_zs, had_data)
23422336

23432337
return patches

lib/mpl_toolkits/mplot3d/proj3d.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,9 @@ def proj_points(points, M):
214214
return list(zip(*proj_trans_points(points, M)))
215215

216216
def proj_trans_points(points, M):
217+
"""
218+
Apply transformation matrix M on a set of points
219+
"""
217220
xs, ys, zs = list(zip(*points))
218221
return proj_transform(xs, ys, zs, M)
219222

0 commit comments

Comments
 (0)