Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork8.1k
Description
Summary
I am opening this issue in order to discuss the possibility of enhancing the API ofdraw_path_collection() for the long term. This was initially discussed inthis and the following comments by@anntzer and@timhoffm.
Adding/ vectorizing new attributes in collections requires changing the signature ofdraw_path_collection(). This causes incompatibility with third party backends, such asmplcairo , because they still use the older signature.
To workaround this in#29044 (that added thehatchcolors argument) a provisional API was used, which relies on making an empty call todraw_path_collection() to infer the signature. This current API is temporary and requires a rewrite.
Proposed fix
A solution proposed by@anntzer inthis comment is to use an API similar todraw_path() - send parameters through a dataclass and use getters to retrieve the required parameters, analogous toGraphicsContextBase indraw_path().
We could call this dataclass something likeVectorizedGraphicsContextBase . It is similar toGraphicsContextBase, but each attribute is vectorized. A simple draft of the proposed solution is shown below.
Why is this change needed?
This would cause a one time break for third party backends. However, in the long term, it would become much simpler to add / vectorize new attributes in collections, without completely breaking third party backends. For example, it would greatly simplify#27937, that aims to vectorize thehatch attribute.
Code Draft
# backend_bases.pyclassVectorizedGraphicsContextBase:def__init__(self):# vectorized attributesself._facecolor= [(0.0,0.0,0.0,1.0)]self._rgb= [(0.0,0.0,0.0,1.0)]self._linewidth= [1] ...# non-vectorized attributesself._cliprect=Noneself._clippath=None ...
# collections.py# `vgc` is an instance of VectorizedGraphicsComtextBasedefdraw(self,renderer): ...transform,offset_trf,offsets,paths=self._prepare_points()vgc=renderer.new_vgc()self._set_gc_clip(vgc)vgc._facecolor=self.get_facecolor()vgc._rgb=self.get_edgecolor()vgc._linewidth=self.get_linewidth() ...renderer.draw_path_collection(vgc,transform.frozen(),paths,self.get_transforms(),offsets,offset_trf)
# backend_bases.pydefdraw_path_collection(self,vgc,master_transform,paths,all_transforms,offsets,offset_trans):path_ids=self._iter_collection_raw_paths(master_transform,paths,all_transforms )forxo,yo,path_id,gc,rgbFaceinself._iter_collection(vgc,list(path_ids),offsets,offset_trans ):path,transform=path_idifxo!=0oryo!=0:transform=transform.frozen()transform.translate(xo,yo)self.draw_path(gc,path,transform,rgbFace)def_iter_collection(self,vgc,path_ids,offsets,offset_trans):Npaths=len(path_ids)Noffsets=len(offsets)N=max(Npaths,Noffsets)Nfacecolors=len(vgc._facecolor)Nedgecolors=len(vgc._rgb)Nlinewidths=len(vgc._linewidth)pathids=cycle_or_default(path_ids)toffsets=cycle_or_default(offset_trans.transform(offsets), (0,0))fcs=cycle_or_default(vgc._facecolor)ecs=cycle_or_default(vgc._rgb)lws=cycle_or_default(vgc._linewidth)gc=self.new_gc()# attributes that are not vectorizedgc._clip_path=vgc._clip_pathgc._clip_rect=vgc._clip_rect ...forpathid, (xo,yo),fc,ec,lwinitertools.islice(zip(pathids,toffsets,fcs,ecs,lws),N ):ifnot (np.isfinite(xo)andnp.isfinite(yo)):continueifNedgecolors:ifNlinewidths:gc.set_linewidth(lw)iflen(ec)==4andec[3]==0.0:gc.set_linewidth(0)else:gc.set_foreground(ec)iffcisnotNoneandlen(fc)==4andfc[3]==0:fc=None ...yieldxo,yo,pathid,gc,fcgc.restore()