23
23
24
24
import six
25
25
26
+ import copy
26
27
import os , sys , warnings , gzip
27
28
28
29
import numpy as np
@@ -93,35 +94,41 @@ def buffer_info(self):
93
94
94
95
95
96
def _convert_path (ctx , path , transform , clip = None ):
97
+ return _convert_paths (ctx , [path ], [transform ], clip )
98
+
99
+
100
+ def _convert_paths (ctx , paths , transforms , clip = None ):
96
101
if HAS_CAIRO_CFFI :
97
102
try :
98
- return _convert_path_fast (ctx , path , transform , clip )
103
+ return _convert_paths_fast (ctx , paths , transforms , clip )
99
104
except NotImplementedError :
100
105
pass
101
- return _convert_path_slow (ctx , path , transform , clip )
102
-
103
-
104
- def _convert_path_slow (ctx , path , transform , clip = None ):
105
- for points , code in path .iter_segments (transform , clip = clip ):
106
- if code == Path .MOVETO :
107
- ctx .move_to (* points )
108
- elif code == Path .CLOSEPOLY :
109
- ctx .close_path ()
110
- elif code == Path .LINETO :
111
- ctx .line_to (* points )
112
- elif code == Path .CURVE3 :
113
- ctx .curve_to (points [0 ], points [1 ],
114
- points [0 ], points [1 ],
115
- points [2 ], points [3 ])
116
- elif code == Path .CURVE4 :
117
- ctx .curve_to (* points )
118
-
119
-
120
- def _convert_path_fast (ctx , path , transform , clip = None ):
106
+ return _convert_paths_slow (ctx , paths , transforms , clip )
107
+
108
+
109
+ def _convert_paths_slow (ctx , paths , transforms , clip = None ):
110
+ for path , transform in zip (paths , transforms ):
111
+ for points , code in path .iter_segments (transform , clip = clip ):
112
+ if code == Path .MOVETO :
113
+ ctx .move_to (* points )
114
+ elif code == Path .CLOSEPOLY :
115
+ ctx .close_path ()
116
+ elif code == Path .LINETO :
117
+ ctx .line_to (* points )
118
+ elif code == Path .CURVE3 :
119
+ ctx .curve_to (points [0 ], points [1 ],
120
+ points [0 ], points [1 ],
121
+ points [2 ], points [3 ])
122
+ elif code == Path .CURVE4 :
123
+ ctx .curve_to (* points )
124
+
125
+
126
+ def _convert_paths_fast (ctx , paths , transforms , clip = None ):
121
127
ffi = cairo .ffi
122
- cleaned = path .cleaned (transform = transform , clip = clip )
123
- vertices = cleaned .vertices
124
- codes = cleaned .codes
128
+ cleaneds = [path .cleaned (transform = transform , clip = clip )
129
+ for path , transform in zip (paths , transforms )]
130
+ vertices = np .concatenate ([cleaned .vertices for cleaned in cleaneds ])
131
+ codes = np .concatenate ([cleaned .codes for cleaned in cleaneds ])
125
132
126
133
# TODO: Implement Bezier degree elevation formula. Note that the "slow"
127
134
# implementation is, in fact, also incorrect...
@@ -130,10 +137,8 @@ def _convert_path_fast(ctx, path, transform, clip=None):
130
137
# Remove unused vertices and convert to cairo codes. Note that unlike
131
138
# cairo_close_path, we do not explicitly insert an extraneous MOVE_TO after
132
139
# CLOSE_PATH, so our resulting buffer may be smaller.
133
- if codes [- 1 ] == Path .STOP :
134
- codes = codes [:- 1 ]
135
- vertices = vertices [:- 1 ]
136
- vertices = vertices [codes != Path .CLOSEPOLY ]
140
+ vertices = vertices [(codes != Path .STOP ) & (codes != Path .CLOSEPOLY )]
141
+ codes = codes [codes != Path .STOP ]
137
142
codes = _MPL_TO_CAIRO_PATH_TYPE [codes ]
138
143
# Where are the headers of each cairo portions?
139
144
cairo_type_sizes = _CAIRO_PATH_TYPE_SIZES [codes ]
@@ -278,6 +283,54 @@ def draw_markers(self, gc, marker_path, marker_trans, path, transform, rgbFace=N
278
283
if not filled :
279
284
self ._fill_and_stroke (ctx , rgbFace , gc .get_alpha (), gc .get_forced_alpha ())
280
285
286
+ def draw_path_collection (
287
+ self , gc , master_transform , paths , all_transforms , offsets ,
288
+ offsetTrans , facecolors , edgecolors , linewidths , linestyles ,
289
+ antialiaseds , urls , offset_position ):
290
+
291
+ path_ids = []
292
+ for path , transform in self ._iter_collection_raw_paths (
293
+ master_transform , paths , all_transforms ):
294
+ path_ids .append ((path , Affine2D (transform )))
295
+
296
+ reuse_key = None
297
+ grouped_draw = []
298
+
299
+ def _draw_paths ():
300
+ if not grouped_draw :
301
+ return
302
+ gc_vars , rgb_fc = reuse_key
303
+ gc = copy .copy (gc0 )
304
+ vars (gc ).update (gc_vars )
305
+ for k , v in gc_vars .items ():
306
+ try :
307
+ getattr (gc , "set" + k )(v )
308
+ except (AttributeError , TypeError ):
309
+ pass
310
+ gc .ctx .new_path ()
311
+ paths , transforms = zip (* grouped_draw )
312
+ grouped_draw .clear ()
313
+ _convert_paths (gc .ctx , paths , transforms )
314
+ self ._fill_and_stroke (
315
+ gc .ctx , rgb_fc , gc .get_alpha (), gc .get_forced_alpha ())
316
+
317
+ for xo , yo , path_id , gc0 , rgb_fc in self ._iter_collection (
318
+ gc , master_transform , all_transforms , path_ids , offsets ,
319
+ offsetTrans , facecolors , edgecolors , linewidths , linestyles ,
320
+ antialiaseds , urls , offset_position ):
321
+ path , transform = path_id
322
+ transform = (Affine2D (transform .get_matrix ()).translate (xo , yo )
323
+ + Affine2D ().scale (1 , - 1 ).translate (0 , self .height ))
324
+ # rgb_fc could be a ndarray, for which equality is elementwise.
325
+ new_key = vars (gc0 ), tuple (rgb_fc ) if rgb_fc is not None else None
326
+ if new_key == reuse_key :
327
+ grouped_draw .append ((path , transform ))
328
+ else :
329
+ _draw_paths ()
330
+ grouped_draw .append ((path , transform ))
331
+ reuse_key = new_key
332
+ _draw_paths ()
333
+
281
334
def draw_image (self , gc , x , y , im ):
282
335
# bbox - not currently used
283
336
if sys .byteorder == 'little' :
0 commit comments