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