@@ -312,10 +312,84 @@ def draw_bar(self, coll):
312312 "assuming data redundancy, not plotting."
313313 )
314314
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+
315388 def draw_marked_line (self , ** props ):
316389 """Create a data dict for a line obj.
317390
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.
319393
320394 props.keys() -- [
321395 'coordinates', ('data', 'axes', 'figure', or 'display')
@@ -346,7 +420,7 @@ def draw_marked_line(self, **props):
346420
347421 """
348422 self .msg += " Attempting to draw a line "
349- line , marker = {}, {}
423+ line , marker , shape = {}, {}, {}
350424 if props ["linestyle" ] and props ["markerstyle" ]:
351425 self .msg += "... with both lines+markers\n "
352426 mode = "lines+markers"
@@ -361,23 +435,43 @@ def draw_marked_line(self, **props):
361435 props ["linestyle" ]["color" ], props ["linestyle" ]["alpha" ]
362436 )
363437
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+ )
370452 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+ )
381475 if props ["coordinates" ] == "data" :
382476 marked_line = go .Scatter (
383477 mode = mode ,
@@ -404,6 +498,9 @@ def draw_marked_line(self, **props):
404498 )
405499 self .plotly_fig .add_trace (marked_line ),
406500 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 )
407504 else :
408505 self .msg += " Line didn't have 'data' coordinates, " "not drawing\n "
409506 warnings .warn (
0 commit comments