diff --git a/_examples/axes_labels/output.png b/_examples/axes_labels/output.png new file mode 100644 index 0000000..5f93731 Binary files /dev/null and b/_examples/axes_labels/output.png differ diff --git a/_examples/request_timings/main.go b/_examples/request_timings/main.go index 47c29f9..dac7e28 100644 --- a/_examples/request_timings/main.go +++ b/_examples/request_timings/main.go @@ -72,6 +72,11 @@ func drawChart(res http.ResponseWriter, req *http.Request) { graph := chart.Chart{ Width: 1280, Height: 720, + Background: chart.Style{ + Padding: chart.Box{ + Top: 50, + }, + }, YAxis: chart.YAxis{ Name: "Elapsed Millis", NameStyle: chart.StyleShow(), @@ -96,7 +101,7 @@ func drawChart(res http.ResponseWriter, req *http.Request) { }, } - graph.Elements = []chart.Renderable{chart.Legend(&graph)} + graph.Elements = []chart.Renderable{chart.LegendThin(&graph)} res.Header().Set("Content-Type", "image/png") graph.Render(chart.PNG, res) diff --git a/legend.go b/legend.go index 9425c9a..6722a6d 100644 --- a/legend.go +++ b/legend.go @@ -82,17 +82,18 @@ func Legend(c *Chart, userDefaults ...Style) Renderable { ycursor := legendContent.Top tx := legendContent.Left legendCount := 0 + var label string for x := 0; x < len(labels); x++ { - if len(labels[x]) > 0 { - + label = labels[x] + if len(label) > 0 { if legendCount > 0 { ycursor += DefaultMinimumTickVerticalSpacing } - tb := r.MeasureText(labels[x]) + tb := r.MeasureText(label) ty := ycursor + tb.Height() - r.Text(labels[x], tx, ty) + r.Text(label, tx, ty) th2 := tb.Height() >> 1 @@ -114,3 +115,101 @@ func Legend(c *Chart, userDefaults ...Style) Renderable { } } } + +// 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) { + legendDefaults := Style{ + FillColor: drawing.ColorWhite, + FontColor: DefaultTextColor, + FontSize: 8.0, + StrokeColor: DefaultAxisColor, + StrokeWidth: DefaultAxisLineWidth, + Padding: Box{ + Top: 2, + Left: 7, + Right: 7, + Bottom: 5, + }, + } + + var legendStyle Style + if len(userDefaults) > 0 { + legendStyle = userDefaults[0].InheritFrom(chartDefaults.InheritFrom(legendDefaults)) + } else { + legendStyle = chartDefaults.InheritFrom(legendDefaults) + } + + r.SetFont(legendStyle.GetFont()) + r.SetFontColor(legendStyle.GetFontColor()) + r.SetFontSize(legendStyle.GetFontSize()) + + var labels []string + var lines []Style + for index, s := range c.Series { + if s.GetStyle().IsZero() || s.GetStyle().Show { + if _, isAnnotationSeries := s.(AnnotationSeries); !isAnnotationSeries { + labels = append(labels, s.GetName()) + lines = append(lines, s.GetStyle().InheritFrom(c.styleDefaultsSeries(index))) + } + } + } + + var textHeight int + var textWidth int + var textBox Box + for x := 0; x < len(labels); x++ { + if len(labels[x]) > 0 { + textBox = r.MeasureText(labels[x]) + textHeight = Math.MaxInt(textBox.Height(), textHeight) + textWidth = Math.MaxInt(textBox.Width(), textWidth) + } + } + + legendBoxHeight := textHeight + legendStyle.Padding.Top + legendStyle.Padding.Bottom + chartPadding := cb.Top + legendYMargin := (chartPadding - legendBoxHeight) >> 1 + + legendBox := Box{ + Left: cb.Left, + Right: cb.Right, + Top: legendYMargin, + Bottom: legendYMargin + legendBoxHeight, + } + + Draw.Box(r, legendBox, legendDefaults) + + r.SetFont(legendStyle.GetFont()) + r.SetFontColor(legendStyle.GetFontColor()) + r.SetFontSize(legendStyle.GetFontSize()) + + lineTextGap := 5 + lineLengthMinimum := 25 + + tx := legendBox.Left + legendStyle.Padding.Left + ty := legendYMargin + legendStyle.Padding.Top + textHeight + var label string + var lx, ly int + th2 := textHeight >> 1 + for index := range labels { + label = labels[index] + if len(label) > 0 { + textBox = r.MeasureText(label) + r.Text(label, tx, ty) + + lx = tx + textBox.Width() + lineTextGap + ly = ty - th2 + + r.SetStrokeColor(lines[index].GetStrokeColor()) + r.SetStrokeWidth(lines[index].GetStrokeWidth()) + r.SetStrokeDashArray(lines[index].GetStrokeDashArray()) + + r.MoveTo(lx, ly) + r.LineTo(lx+lineLengthMinimum, ly) + r.Stroke() + + tx += textBox.Width() + DefaultMinimumTickHorizontalSpacing + lineTextGap + lineLengthMinimum + } + } + } +}