@@ -228,12 +228,12 @@ def get_path(hatchpattern, density=6):
228
228
attrs = [
229
229
# ("color", "black"), For now it is an attr of gc
230
230
# ("alpha", 1.0),
231
- ('scale' , 6.0 ),
231
+ ('scale' , 6 ),
232
232
('weight' , 1.0 ),
233
233
('angle' , 0.0 ),
234
234
('random_rotation' , False ),
235
235
('staggered' , False ),
236
- ('random_placement ' , False ),
236
+ ('filled ' , True ),
237
237
]
238
238
239
239
@@ -242,16 +242,19 @@ def __init__(self, hatchpattern, **kwargs):
242
242
self .hatchpattern = hatchpattern
243
243
self .kwargs = {attr : kwargs .get (attr , default ) for attr , default in attrs }
244
244
245
- def rotate_path (self , vertices , angle = None ):
245
+ def rotate_path (self , vertices , angle = None , scale_correction = True ):
246
+ vertices = vertices .copy ()
247
+
246
248
if angle is None :
247
249
angle = self .kwargs ["angle" ]
248
250
angle_rad = np .deg2rad (angle )
249
251
250
252
center = np .mean (vertices , axis = 0 )
251
253
vertices -= center
252
254
253
- scaling = np .sin (angle_rad ) + np .cos (angle_rad )
254
- vertices *= scaling
255
+ if scale_correction :
256
+ scaling = abs (np .sin (angle_rad )) + abs (np .cos (angle_rad ))
257
+ vertices *= scaling
255
258
256
259
rotation_matrix = np .array (
257
260
[
@@ -267,19 +270,85 @@ def get_vertices_and_codes(self, hatch_buffer_size=100):
267
270
self .hatch_buffer_size = hatch_buffer_size
268
271
vertices , codes = np .empty ((0 , 2 )), np .empty (0 , Path .code_type )
269
272
270
- for hatchpattern in self .hatchpattern :
271
- func = hatchpatterns [hatchpattern ]
272
- for f in func :
273
- verts , cods = f (self )
274
- vertices = np .concatenate ((vertices , verts ))
275
- codes = np .concatenate ((codes , cods ))
273
+ if self .hatchpattern in hatchpatterns :
274
+ # This is for line hatches
275
+ for func in np .atleast_1d (hatchpatterns [self .hatchpattern ]):
276
+ vertices_part , codes_part = func (self )
277
+ vertices_part = self .rotate_path (vertices_part )
278
+
279
+ vertices = np .concatenate ((vertices , vertices_part ))
280
+ codes = np .concatenate ((codes , codes_part ))
281
+ else :
282
+ # This is for marker hatches
283
+ if self .hatchpattern not in MarkerHatchStyle .marker_paths :
284
+ raise ValueError (f"Unknown hatch pattern: { self .hatchpattern } " )
285
+ func = MarkerHatchStyle .marker_pattern
286
+ vertices_part , codes_part = func (self )
287
+
288
+ vertices = np .concatenate ((vertices , vertices_part ))
289
+ codes = np .concatenate ((codes , codes_part ))
276
290
277
- vertices = self .rotate_path (vertices )
278
291
return vertices , codes
279
292
280
293
281
294
class MarkerHatchStyle (HatchStyle ):
282
- pass
295
+ marker_paths = {
296
+ 'o' : Path .unit_circle ,
297
+ 'O' : Path .unit_circle ,
298
+ '*' : (Path .unit_regular_star , 5 ), # TODO: is there a better way to do this?
299
+ }
300
+
301
+ # TODO: saner defaults or no?
302
+ marker_sizes = {
303
+ 'o' : 0.2 ,
304
+ 'O' : 0.35 ,
305
+ '*' : 1.0 / 3.0 ,
306
+ }
307
+
308
+ def _get_marker_path (marker ):
309
+ func = np .atleast_1d (MarkerHatchStyle .marker_paths [marker ])
310
+ path = func [0 ](* func [1 :])
311
+ size = MarkerHatchStyle .marker_sizes [marker ]
312
+
313
+ return Path (
314
+ vertices = path .vertices * size ,
315
+ codes = path .codes ,
316
+ )
317
+
318
+ def marker_pattern (hatchstyle ):
319
+ size = hatchstyle .kwargs ['weight' ]
320
+ num_rows = round (
321
+ hatchstyle .kwargs ['scale' ] * hatchstyle .hatch_buffer_size / 100.0
322
+ )
323
+ path = MarkerHatchStyle ._get_marker_path (hatchstyle .hatchpattern )
324
+ shape_vertices = hatchstyle .rotate_path (path .vertices , scale_correction = False )
325
+ shape_codes = path .codes
326
+
327
+ offset = 1.0 / num_rows
328
+ shape_vertices = shape_vertices * offset * size
329
+
330
+ if not hatchstyle .kwargs ['filled' ]:
331
+ shape_vertices = np .concatenate (
332
+ [shape_vertices , shape_vertices [::- 1 ] * 0.9 ]
333
+ )
334
+ shape_codes = np .concatenate ([shape_codes , shape_codes ])
335
+
336
+ vertices_parts = []
337
+ codes_parts = []
338
+ for row in range (num_rows + 1 ):
339
+ if row % 2 == 0 :
340
+ cols = np .linspace (0 , 1 , num_rows + 1 )
341
+ else :
342
+ cols = np .linspace (offset / 2 , 1 - offset / 2 , num_rows )
343
+ row_pos = row * offset
344
+ for col_pos in cols :
345
+ vertices_parts .append (shape_vertices + [col_pos , row_pos ])
346
+ codes_parts .append (shape_codes )
347
+
348
+ vertices = np .concatenate (vertices_parts )
349
+ codes = np .concatenate (codes_parts )
350
+
351
+ return vertices , codes
283
352
284
353
285
354
class LineHatchStyle (HatchStyle ):
@@ -371,10 +440,10 @@ def south_east(hatchstyle):
371
440
372
441
373
442
hatchpatterns = {
374
- "-" : ( LineHatchStyle .horizontal ,) ,
375
- "|" : ( LineHatchStyle .vertical ,) ,
376
- "/" : ( LineHatchStyle .north_east ,) ,
377
- " \\ " : ( LineHatchStyle .south_east ,) ,
378
- "+" : (LineHatchStyle .horizontal , LineHatchStyle .vertical ),
379
- "x" : (LineHatchStyle .north_east , LineHatchStyle .south_east ),
443
+ '-' : LineHatchStyle .horizontal ,
444
+ '|' : LineHatchStyle .vertical ,
445
+ '/' : LineHatchStyle .north_east ,
446
+ ' \\ ' : LineHatchStyle .south_east ,
447
+ '+' : (LineHatchStyle .horizontal , LineHatchStyle .vertical ),
448
+ 'x' : (LineHatchStyle .north_east , LineHatchStyle .south_east ),
380
449
}
0 commit comments