@@ -312,10 +312,84 @@ def draw_bar(self, coll):
312
312
"assuming data redundancy, not plotting."
313
313
)
314
314
315
+ def draw_legend_shapes (self , mode , shape , ** props ):
316
+ """Create a shape that matches lines or markers in legends.
317
+
318
+ Main issue is that path for circles do not render, so we have to use 'circle'
319
+ instead of 'path'.
320
+ """
321
+ for single_mode in mode .split ("+" ):
322
+ x = props ["data" ][0 ][0 ]
323
+ y = props ["data" ][0 ][1 ]
324
+ if single_mode == "markers" and props .get ("markerstyle" ):
325
+ size = shape .pop ("size" , 6 )
326
+ symbol = shape .pop ("symbol" )
327
+ # aligning to "center"
328
+ x0 = 0
329
+ y0 = 0
330
+ x1 = size
331
+ y1 = size
332
+ markerpath = props ["markerstyle" ].get ("markerpath" )
333
+ if markerpath is None and symbol != "circle" :
334
+ self .msg += "not sure how to handle this marker without a valid path\n "
335
+ return
336
+ # marker path to SVG path conversion
337
+ path = ' ' .join ([f"{ a } { t [0 ]} ,{ t [1 ]} " for a , t in zip (markerpath [1 ], markerpath [0 ])])
338
+
339
+ if symbol == "circle" :
340
+ # symbols like . and o in matplotlib, use circle
341
+ # plotly also maps many other markers to circle, such as 1,8 and p
342
+ path = None
343
+ shape_type = "circle"
344
+ x0 = - size / 2
345
+ y0 = size / 2
346
+ x1 = size / 2
347
+ y1 = size + size / 2
348
+ else :
349
+ # triangles, star etc
350
+ shape_type = "path"
351
+ legend_shape = go .layout .Shape (
352
+ type = shape_type ,
353
+ xref = "paper" ,
354
+ yref = "paper" ,
355
+ x0 = x0 ,
356
+ y0 = y0 ,
357
+ x1 = x1 ,
358
+ y1 = y1 ,
359
+ xsizemode = "pixel" ,
360
+ ysizemode = "pixel" ,
361
+ xanchor = x ,
362
+ yanchor = y ,
363
+ path = path ,
364
+ ** shape
365
+ )
366
+
367
+ elif single_mode == "lines" :
368
+ mode = "line"
369
+ x1 = props ["data" ][1 ][0 ]
370
+ y1 = props ["data" ][1 ][1 ]
371
+
372
+ legend_shape = go .layout .Shape (
373
+ type = mode ,
374
+ xref = "paper" ,
375
+ yref = "paper" ,
376
+ x0 = x ,
377
+ y0 = y + 0.02 ,
378
+ x1 = x1 ,
379
+ y1 = y1 + 0.02 ,
380
+ ** shape
381
+ )
382
+ else :
383
+ self .msg += "not sure how to handle this element\n "
384
+ return
385
+ self .plotly_fig .add_shape (legend_shape )
386
+ self .msg += " Heck yeah, I drew that shape\n "
387
+
315
388
def draw_marked_line (self , ** props ):
316
389
"""Create a data dict for a line obj.
317
390
318
- This will draw 'lines', 'markers', or 'lines+markers'.
391
+ This will draw 'lines', 'markers', or 'lines+markers'. For legend elements,
392
+ this will use layout.shapes, so they can be positioned with paper refs.
319
393
320
394
props.keys() -- [
321
395
'coordinates', ('data', 'axes', 'figure', or 'display')
@@ -346,7 +420,7 @@ def draw_marked_line(self, **props):
346
420
347
421
"""
348
422
self .msg += " Attempting to draw a line "
349
- line , marker = {}, {}
423
+ line , marker , shape = {}, {}, {}
350
424
if props ["linestyle" ] and props ["markerstyle" ]:
351
425
self .msg += "... with both lines+markers\n "
352
426
mode = "lines+markers"
@@ -361,23 +435,43 @@ def draw_marked_line(self, **props):
361
435
props ["linestyle" ]["color" ], props ["linestyle" ]["alpha" ]
362
436
)
363
437
364
- # print(mpltools.convert_dash(props['linestyle']['dasharray']))
365
- line = go .scatter .Line (
366
- color = color ,
367
- width = props ["linestyle" ]["linewidth" ],
368
- dash = mpltools .convert_dash (props ["linestyle" ]["dasharray" ]),
369
- )
438
+ if props ["coordinates" ] == "data" :
439
+ line = go .scatter .Line (
440
+ color = color ,
441
+ width = props ["linestyle" ]["linewidth" ],
442
+ dash = mpltools .convert_dash (props ["linestyle" ]["dasharray" ]),
443
+ )
444
+ else :
445
+ shape = dict (
446
+ line = dict (
447
+ color = color ,
448
+ width = props ["linestyle" ]["linewidth" ],
449
+ dash = mpltools .convert_dash (props ["linestyle" ]["dasharray" ])
450
+ )
451
+ )
370
452
if props ["markerstyle" ]:
371
- marker = go .scatter .Marker (
372
- opacity = props ["markerstyle" ]["alpha" ],
373
- color = props ["markerstyle" ]["facecolor" ],
374
- symbol = mpltools .convert_symbol (props ["markerstyle" ]["marker" ]),
375
- size = props ["markerstyle" ]["markersize" ],
376
- line = dict (
377
- color = props ["markerstyle" ]["edgecolor" ],
378
- width = props ["markerstyle" ]["edgewidth" ],
379
- ),
380
- )
453
+ if props ["coordinates" ] == "data" :
454
+ marker = go .scatter .Marker (
455
+ opacity = props ["markerstyle" ]["alpha" ],
456
+ color = props ["markerstyle" ]["facecolor" ],
457
+ symbol = mpltools .convert_symbol (props ["markerstyle" ]["marker" ]),
458
+ size = props ["markerstyle" ]["markersize" ],
459
+ line = dict (
460
+ color = props ["markerstyle" ]["edgecolor" ],
461
+ width = props ["markerstyle" ]["edgewidth" ],
462
+ ),
463
+ )
464
+ else :
465
+ shape = dict (
466
+ opacity = props ["markerstyle" ]["alpha" ],
467
+ fillcolor = props ["markerstyle" ]["facecolor" ],
468
+ symbol = mpltools .convert_symbol (props ["markerstyle" ]["marker" ]),
469
+ size = props ["markerstyle" ]["markersize" ],
470
+ line = dict (
471
+ color = props ["markerstyle" ]["edgecolor" ],
472
+ width = props ["markerstyle" ]["edgewidth" ],
473
+ ),
474
+ )
381
475
if props ["coordinates" ] == "data" :
382
476
marked_line = go .Scatter (
383
477
mode = mode ,
@@ -404,6 +498,9 @@ def draw_marked_line(self, **props):
404
498
)
405
499
self .plotly_fig .add_trace (marked_line ),
406
500
self .msg += " Heck yeah, I drew that line\n "
501
+ elif props ["coordinates" ] == "axes" :
502
+ # dealing with legend graphical elements
503
+ self .draw_legend_shapes (mode = mode ,shape = shape , ** props )
407
504
else :
408
505
self .msg += " Line didn't have 'data' coordinates, " "not drawing\n "
409
506
warnings .warn (
0 commit comments