Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit1e08084

Browse files
committed
Store vertices as (masked) arrays in Poly3DCollection
This removes a bunch of very expensive list comprehensions, droppingrendering times by half for some 3D surface plots.
1 parentef9fc20 commit1e08084

File tree

2 files changed

+76
-30
lines changed

2 files changed

+76
-30
lines changed

‎lib/mpl_toolkits/mplot3d/art3d.py

Lines changed: 46 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -598,16 +598,33 @@ def set_zsort(self, zsort):
598598
self.stale=True
599599

600600
defget_vector(self,segments3d):
601-
"""Optimize points for projection."""
602-
iflen(segments3d):
603-
xs,ys,zs=np.row_stack(segments3d).T
604-
else:# row_stack can't stack zero arrays.
605-
xs,ys,zs= [], [], []
606-
ones=np.ones(len(xs))
607-
self._vec=np.array([xs,ys,zs,ones])
601+
"""
602+
Optimize points for projection.
608603
609-
indices= [0,*np.cumsum([len(segment)forsegmentinsegments3d])]
610-
self._segslices= [*map(slice,indices[:-1],indices[1:])]
604+
Parameters
605+
----------
606+
segments3d : NumPy array or list of NumPy arrays
607+
List of vertices of the boundary of every segment. If all paths are
608+
of equal length and this argument is a NumPy arrray, then it should
609+
be of shape (num_faces, num_vertices, 3).
610+
"""
611+
ifisinstance(segments3d,np.ndarray):
612+
ifsegments3d.ndim!=3orsegments3d.shape[-1]!=3:
613+
raiseValueError("segments3d must be a MxNx3 array, but got "+
614+
"shape {}".format(segments3d.shape))
615+
self._segments=segments3d
616+
else:
617+
num_faces=len(segments3d)
618+
num_verts=np.fromiter(map(len,segments3d),dtype=np.intp)
619+
max_verts=num_verts.max(initial=0)
620+
padded=np.empty((num_faces,max_verts,3))
621+
fori,faceinenumerate(segments3d):
622+
padded[i, :len(face)]=face
623+
mask=np.arange(max_verts)>=num_verts[:,None]
624+
mask=mask[...,None]# add a component axis
625+
# ma.array does not broadcast the mask for us
626+
mask=np.broadcast_to(mask,padded.shape)
627+
self._segments=np.ma.array(padded,mask=mask)
611628

612629
defset_verts(self,verts,closed=True):
613630
"""Set 3D vertices."""
@@ -649,37 +666,39 @@ def do_3d_projection(self, renderer):
649666
self.update_scalarmappable()
650667
self._facecolors3d=self._facecolors
651668

652-
txs,tys,tzs=proj3d._proj_transform_vec(self._vec,renderer.M)
653-
xyzlist=[(txs[sl],tys[sl],tzs[sl])forslinself._segslices]
669+
psegments=proj3d._proj_transform_vectors(self._segments,renderer.M)
670+
num_faces=len(psegments)
654671

655672
# This extra fuss is to re-order face / edge colors
656673
cface=self._facecolors3d
657674
cedge=self._edgecolors3d
658-
iflen(cface)!=len(xyzlist):
659-
cface=cface.repeat(len(xyzlist),axis=0)
660-
iflen(cedge)!=len(xyzlist):
675+
iflen(cface)!=num_faces:
676+
cface=cface.repeat(num_faces,axis=0)
677+
iflen(cedge)!=num_faces:
661678
iflen(cedge)==0:
662679
cedge=cface
663680
else:
664-
cedge=cedge.repeat(len(xyzlist),axis=0)
681+
cedge=cedge.repeat(num_faces,axis=0)
665682

666-
# sort by depth (furthest drawn first)
667-
z_segments_2d=sorted(
668-
((self._zsortfunc(zs),np.column_stack([xs,ys]),fc,ec,idx)
669-
foridx, ((xs,ys,zs),fc,ec)
670-
inenumerate(zip(xyzlist,cface,cedge))),
671-
key=lambdax:x[0],reverse=True)
683+
face_z=self._zsortfunc(psegments[...,2],axis=-1)
684+
ifisinstance(face_z,np.ma.MaskedArray):
685+
# NOTE: Unpacking .data is safe here, because every face has to
686+
# contain a valid vertex.
687+
face_z=face_z.data
688+
face_order=np.argsort(face_z,axis=-1)[::-1]
672689

673-
segments_2d= [sforz,s,fc,ec,idxinz_segments_2d]
674690
ifself._codes3disnotNone:
675-
codes= [self._codes3d[idx]forz,s,fc,ec,idxinz_segments_2d]
691+
segments_2d= [s.data[~s.mask.all(axis=-1)]
692+
forsinpsegments[face_order, :, :2]]
693+
codes= [self._codes3d[idx]foridxinface_order]
676694
PolyCollection.set_verts_and_codes(self,segments_2d,codes)
677695
else:
696+
segments_2d=psegments[face_order, :, :2]
678697
PolyCollection.set_verts(self,segments_2d,self._closed)
679698

680-
self._facecolors2d=[fcforz,s,fc,ec,idxinz_segments_2d]
699+
self._facecolors2d=cface[face_order]
681700
iflen(self._edgecolors3d)==len(cface):
682-
self._edgecolors2d=[ecforz,s,fc,ec,idxinz_segments_2d]
701+
self._edgecolors2d=cedge[face_order]
683702
else:
684703
self._edgecolors2d=self._edgecolors3d
685704

@@ -688,11 +707,11 @@ def do_3d_projection(self, renderer):
688707
zvec=np.array([[0], [0], [self._sort_zpos], [1]])
689708
ztrans=proj3d._proj_transform_vec(zvec,renderer.M)
690709
returnztrans[2][0]
691-
eliftzs.size>0:
710+
elifpsegments.size>0:
692711
# FIXME: Some results still don't look quite right.
693712
# In particular, examine contourf3d_demo2.py
694713
# with az = -54 and elev = -45.
695-
returnnp.min(tzs)
714+
returnnp.min(psegments[...,2])
696715
else:
697716
returnnp.nan
698717

‎lib/mpl_toolkits/mplot3d/proj3d.py

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,10 +103,8 @@ def ortho_transformation(zfront, zback):
103103

104104
def_proj_transform_vec(vec,M):
105105
vecw=np.dot(M,vec)
106-
w=vecw[3]
107106
# clip here..
108-
txs,tys,tzs=vecw[0]/w,vecw[1]/w,vecw[2]/w
109-
returntxs,tys,tzs
107+
returnvecw[:3]/vecw[3]
110108

111109

112110
def_proj_transform_vec_clip(vec,M):
@@ -146,6 +144,35 @@ def proj_transform(xs, ys, zs, M):
146144
transform=proj_transform
147145

148146

147+
def_proj_transform_vectors(vecs,M):
148+
"""Vector version of ``project_transform`` able to handle MaskedArrays.
149+
150+
Parameters
151+
----------
152+
vecs : ... x 3 np.ndarray or np.ma.MaskedArray
153+
Input vectors
154+
155+
M : 4 x 4 np.ndarray
156+
Projection matrix
157+
"""
158+
vecs_shape=vecs.shape
159+
vecs=vecs.reshape(-1,3).T
160+
161+
is_masked=isinstance(vecs,np.ma.MaskedArray)
162+
ifis_masked:
163+
mask=vecs.mask
164+
165+
vecs_pad=np.empty((vecs.shape[0]+1,)+vecs.shape[1:])
166+
vecs_pad[:-1]=vecs
167+
vecs_pad[-1]=1
168+
product=np.dot(M,vecs_pad)
169+
tvecs=product[:3]/product[3]
170+
171+
ifis_masked:
172+
tvecs=np.ma.array(tvecs,mask=mask)
173+
returntvecs.T.reshape(vecs_shape)
174+
175+
149176
defproj_transform_clip(xs,ys,zs,M):
150177
"""
151178
Transform the points by the projection matrix

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp