From bbad84f537d7db4e26b6bbc9b331bf0bebe8b831 Mon Sep 17 00:00:00 2001 From: Rico Date: Sat, 5 Feb 2022 17:07:04 +0100 Subject: [PATCH] feat: implement legend type with fixed length line on the left side fixes #185 --- legend.go | 118 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) diff --git a/legend.go b/legend.go index fbd48ed..a6c301c 100644 --- a/legend.go +++ b/legend.go @@ -118,6 +118,124 @@ func Legend(c *Chart, userDefaults ...Style) Renderable { } } +// LegendLineLeft is a legend with the line drawn left to the legend text. +func LegendLineLeft(c *Chart, userDefaults ...Style) Renderable { + return func(r Renderer, cb Box, chartDefaults Style) { + legendDefaults := Style{ + FillColor: drawing.ColorWhite, + FontColor: DefaultTextColor, + FontSize: 8.0, + StrokeColor: DefaultAxisColor, + StrokeWidth: DefaultAxisLineWidth, + } + + var legendStyle Style + if len(userDefaults) > 0 { + legendStyle = userDefaults[0].InheritFrom(chartDefaults.InheritFrom(legendDefaults)) + } else { + legendStyle = chartDefaults.InheritFrom(legendDefaults) + } + + // DEFAULTS + legendPadding := Box{ + Top: 5, + Left: 5, + Right: 5, + Bottom: 5, + } + lineTextGap := 5 + lineLengthMinimum := 25 + + var labels []string + var lines []Style + for index, s := range c.Series { + if !s.GetStyle().Hidden { + if _, isAnnotationSeries := s.(AnnotationSeries); !isAnnotationSeries { + labels = append(labels, s.GetName()) + lines = append(lines, s.GetStyle().InheritFrom(c.styleDefaultsSeries(index))) + } + } + } + + legend := Box{ + Top: cb.Top, + Left: cb.Left, + // bottom and right will be sized by the legend content + relevant padding. + } + + legendContent := Box{ + Top: legend.Top + legendPadding.Top, + Left: legend.Left + legendPadding.Left, + Right: legend.Left + legendPadding.Left, + Bottom: legend.Top + legendPadding.Top, + } + + legendStyle.GetTextOptions().WriteToRenderer(r) + + // measure + labelCount := 0 + for x := 0; x < len(labels); x++ { + if len(labels[x]) > 0 { + tb := r.MeasureText(labels[x]) + if labelCount > 0 { + legendContent.Bottom += DefaultMinimumTickVerticalSpacing + } + legendContent.Bottom += tb.Height() + right := legendContent.Left + tb.Width() + lineTextGap + lineLengthMinimum + legendContent.Right = MaxInt(legendContent.Right, right) + labelCount++ + } + } + + legend = legend.Grow(legendContent) + legend.Right = legendContent.Right + legendPadding.Right + legend.Bottom = legendContent.Bottom + legendPadding.Bottom + + Draw.Box(r, legend, legendStyle) + + legendStyle.GetTextOptions().WriteToRenderer(r) + + ycursor := legendContent.Top + lx := legendContent.Left + legendCount := 0 + var label string + for x := 0; x < len(labels); x++ { + label = labels[x] + if len(label) > 0 { + if legendCount > 0 { + ycursor += DefaultMinimumTickVerticalSpacing + } + + // Calculate text dimensions + tb := r.MeasureText(label) + ty := ycursor + tb.Height() + th2 := tb.Height() >> 1 + + // Calculate line x and y coordinates + ly := ty - th2 + + // Calculate line ending x coordinate + lx2 := lx + lineLengthMinimum + + r.SetStrokeColor(lines[x].GetStrokeColor()) + r.SetStrokeWidth(lines[x].GetStrokeWidth()) + r.SetStrokeDashArray(lines[x].GetStrokeDashArray()) + + r.MoveTo(lx, ly) + r.LineTo(lx2, ly) + r.Stroke() + + // Calculate Text starting coordinates + textX := lx2 + lineTextGap + r.Text(label, textX, ty) + + ycursor += tb.Height() + legendCount++ + } + } + } +} + // LegendThin is a legend that doesn't obscure the chart area. func LegendThin(c *Chart, userDefaults ...Style) Renderable { return func(r Renderer, cb Box, chartDefaults Style) {