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