text options!
This commit is contained in:
parent
b3386853bb
commit
d84d6790c0
25 changed files with 526 additions and 287 deletions
|
@ -50,7 +50,7 @@ func (as AnnotationSeries) Measure(r Renderer, canvasBox Box, xrange, yrange Ran
|
||||||
style := a.Style.InheritFrom(seriesStyle)
|
style := a.Style.InheritFrom(seriesStyle)
|
||||||
lx := canvasBox.Left + xrange.Translate(a.XValue)
|
lx := canvasBox.Left + xrange.Translate(a.XValue)
|
||||||
ly := canvasBox.Bottom - yrange.Translate(a.YValue)
|
ly := canvasBox.Bottom - yrange.Translate(a.YValue)
|
||||||
ab := MeasureAnnotation(r, canvasBox, style, lx, ly, a.Label)
|
ab := Draw.MeasureAnnotation(r, canvasBox, style, lx, ly, a.Label)
|
||||||
box.Top = MinInt(box.Top, ab.Top)
|
box.Top = MinInt(box.Top, ab.Top)
|
||||||
box.Left = MinInt(box.Left, ab.Left)
|
box.Left = MinInt(box.Left, ab.Left)
|
||||||
box.Right = MaxInt(box.Right, ab.Right)
|
box.Right = MaxInt(box.Right, ab.Right)
|
||||||
|
@ -68,7 +68,7 @@ func (as AnnotationSeries) Render(r Renderer, canvasBox Box, xrange, yrange Rang
|
||||||
style := a.Style.InheritFrom(seriesStyle)
|
style := a.Style.InheritFrom(seriesStyle)
|
||||||
lx := canvasBox.Left + xrange.Translate(a.XValue)
|
lx := canvasBox.Left + xrange.Translate(a.XValue)
|
||||||
ly := canvasBox.Bottom - yrange.Translate(a.YValue)
|
ly := canvasBox.Bottom - yrange.Translate(a.YValue)
|
||||||
DrawAnnotation(r, canvasBox, style, lx, ly, a.Label)
|
Draw.Annotation(r, canvasBox, style, lx, ly, a.Label)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -114,7 +114,7 @@ func (bbs *BollingerBandsSeries) Render(r Renderer, canvasBox Box, xrange, yrang
|
||||||
FillColor: DefaultAxisColor.WithAlpha(32),
|
FillColor: DefaultAxisColor.WithAlpha(32),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
DrawBoundedSeries(r, canvasBox, xrange, yrange, s, bbs, bbs.GetPeriod())
|
Draw.BoundedSeries(r, canvasBox, xrange, yrange, s, bbs, bbs.GetPeriod())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bbs BollingerBandsSeries) getAverage(valueBuffer *RingBuffer) float64 {
|
func (bbs BollingerBandsSeries) getAverage(valueBuffer *RingBuffer) float64 {
|
||||||
|
|
4
chart.go
4
chart.go
|
@ -391,7 +391,7 @@ func (c Chart) getBackgroundStyle() Style {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Chart) drawBackground(r Renderer) {
|
func (c Chart) drawBackground(r Renderer) {
|
||||||
DrawBox(r, Box{
|
Draw.Box(r, Box{
|
||||||
Right: c.GetWidth(),
|
Right: c.GetWidth(),
|
||||||
Bottom: c.GetHeight(),
|
Bottom: c.GetHeight(),
|
||||||
}, c.getBackgroundStyle())
|
}, c.getBackgroundStyle())
|
||||||
|
@ -402,7 +402,7 @@ func (c Chart) getCanvasStyle() Style {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Chart) drawCanvas(r Renderer, canvasBox Box) {
|
func (c Chart) drawCanvas(r Renderer, canvasBox Box) {
|
||||||
DrawBox(r, canvasBox, c.getCanvasStyle())
|
Draw.Box(r, canvasBox, c.getCanvasStyle())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Chart) drawAxes(r Renderer, canvasBox Box, xrange, yrange, yrangeAlt Range, xticks, yticks, yticksAlt []Tick) {
|
func (c Chart) drawAxes(r Renderer, canvasBox Box, xrange, yrange, yrangeAlt Range, xticks, yticks, yticksAlt []Tick) {
|
||||||
|
|
|
@ -51,5 +51,5 @@ func (cs ContinuousSeries) GetYAxis() YAxisType {
|
||||||
// Render renders the series.
|
// Render renders the series.
|
||||||
func (cs ContinuousSeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
|
func (cs ContinuousSeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
|
||||||
style := cs.Style.InheritFrom(defaults)
|
style := cs.Style.InheritFrom(defaults)
|
||||||
DrawLineSeries(r, canvasBox, xrange, yrange, style, cs)
|
Draw.LineSeries(r, canvasBox, xrange, yrange, style, cs)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
package chart
|
package chart
|
||||||
|
|
||||||
import (
|
import "math"
|
||||||
"math"
|
|
||||||
|
|
||||||
"github.com/wcharczuk/go-chart/drawing"
|
var (
|
||||||
|
// Draw contains helpers for drawing common objects.
|
||||||
|
Draw = &draw{}
|
||||||
)
|
)
|
||||||
|
|
||||||
// DrawLineSeries draws a line series with a renderer.
|
type draw struct{}
|
||||||
func DrawLineSeries(r Renderer, canvasBox Box, xrange, yrange Range, s Style, vs ValueProvider) {
|
|
||||||
|
// LineSeries draws a line series with a renderer.
|
||||||
|
func (d draw) LineSeries(r Renderer, canvasBox Box, xrange, yrange Range, s Style, vs ValueProvider) {
|
||||||
if vs.Len() == 0 {
|
if vs.Len() == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -52,8 +55,8 @@ func DrawLineSeries(r Renderer, canvasBox Box, xrange, yrange Range, s Style, vs
|
||||||
r.Stroke()
|
r.Stroke()
|
||||||
}
|
}
|
||||||
|
|
||||||
// DrawBoundedSeries draws a series that implements BoundedValueProvider.
|
// BoundedSeries draws a series that implements BoundedValueProvider.
|
||||||
func DrawBoundedSeries(r Renderer, canvasBox Box, xrange, yrange Range, s Style, bbs BoundedValueProvider, drawOffsetIndexes ...int) {
|
func (d draw) BoundedSeries(r Renderer, canvasBox Box, xrange, yrange Range, s Style, bbs BoundedValueProvider, drawOffsetIndexes ...int) {
|
||||||
drawOffsetIndex := 0
|
drawOffsetIndex := 0
|
||||||
if len(drawOffsetIndexes) > 0 {
|
if len(drawOffsetIndexes) > 0 {
|
||||||
drawOffsetIndex = drawOffsetIndexes[0]
|
drawOffsetIndex = drawOffsetIndexes[0]
|
||||||
|
@ -106,8 +109,8 @@ func DrawBoundedSeries(r Renderer, canvasBox Box, xrange, yrange Range, s Style,
|
||||||
r.FillStroke()
|
r.FillStroke()
|
||||||
}
|
}
|
||||||
|
|
||||||
// DrawHistogramSeries draws a value provider as boxes from 0.
|
// HistogramSeries draws a value provider as boxes from 0.
|
||||||
func DrawHistogramSeries(r Renderer, canvasBox Box, xrange, yrange Range, s Style, vs ValueProvider, barWidths ...int) {
|
func (d draw) HistogramSeries(r Renderer, canvasBox Box, xrange, yrange Range, s Style, vs ValueProvider, barWidths ...int) {
|
||||||
if vs.Len() == 0 {
|
if vs.Len() == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -129,7 +132,7 @@ func DrawHistogramSeries(r Renderer, canvasBox Box, xrange, yrange Range, s Styl
|
||||||
x := cl + xrange.Translate(vx)
|
x := cl + xrange.Translate(vx)
|
||||||
y := yrange.Translate(vy)
|
y := yrange.Translate(vy)
|
||||||
|
|
||||||
DrawBox(r, Box{
|
d.Box(r, Box{
|
||||||
Top: cb - y0,
|
Top: cb - y0,
|
||||||
Left: x - (barWidth >> 1),
|
Left: x - (barWidth >> 1),
|
||||||
Right: x + (barWidth >> 1),
|
Right: x + (barWidth >> 1),
|
||||||
|
@ -139,7 +142,7 @@ func DrawHistogramSeries(r Renderer, canvasBox Box, xrange, yrange Range, s Styl
|
||||||
}
|
}
|
||||||
|
|
||||||
// MeasureAnnotation measures how big an annotation would be.
|
// MeasureAnnotation measures how big an annotation would be.
|
||||||
func MeasureAnnotation(r Renderer, canvasBox Box, s Style, lx, ly int, label string) Box {
|
func (d draw) MeasureAnnotation(r Renderer, canvasBox Box, s Style, lx, ly int, label string) Box {
|
||||||
r.SetFillColor(s.GetFillColor(DefaultAnnotationFillColor))
|
r.SetFillColor(s.GetFillColor(DefaultAnnotationFillColor))
|
||||||
r.SetStrokeColor(s.GetStrokeColor())
|
r.SetStrokeColor(s.GetStrokeColor())
|
||||||
r.SetStrokeWidth(s.GetStrokeWidth())
|
r.SetStrokeWidth(s.GetStrokeWidth())
|
||||||
|
@ -171,8 +174,8 @@ func MeasureAnnotation(r Renderer, canvasBox Box, s Style, lx, ly int, label str
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DrawAnnotation draws an anotation with a renderer.
|
// Annotation draws an anotation with a renderer.
|
||||||
func DrawAnnotation(r Renderer, canvasBox Box, style Style, lx, ly int, label string) {
|
func (d draw) Annotation(r Renderer, canvasBox Box, style Style, lx, ly int, label string) {
|
||||||
r.SetFillColor(style.GetFillColor())
|
r.SetFillColor(style.GetFillColor())
|
||||||
r.SetStrokeColor(style.GetStrokeColor())
|
r.SetStrokeColor(style.GetStrokeColor())
|
||||||
r.SetStrokeWidth(style.GetStrokeWidth())
|
r.SetStrokeWidth(style.GetStrokeWidth())
|
||||||
|
@ -218,8 +221,8 @@ func DrawAnnotation(r Renderer, canvasBox Box, style Style, lx, ly int, label st
|
||||||
r.Text(label, textX, textY)
|
r.Text(label, textX, textY)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DrawBox draws a box with a given style.
|
// Box draws a box with a given style.
|
||||||
func DrawBox(r Renderer, b Box, s Style) {
|
func (d draw) Box(r Renderer, b Box, s Style) {
|
||||||
r.SetFillColor(s.GetFillColor())
|
r.SetFillColor(s.GetFillColor())
|
||||||
r.SetStrokeColor(s.GetStrokeColor())
|
r.SetStrokeColor(s.GetStrokeColor())
|
||||||
r.SetStrokeWidth(s.GetStrokeWidth(DefaultStrokeWidth))
|
r.SetStrokeWidth(s.GetStrokeWidth(DefaultStrokeWidth))
|
||||||
|
@ -234,7 +237,7 @@ func DrawBox(r Renderer, b Box, s Style) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// DrawText draws text with a given style.
|
// DrawText draws text with a given style.
|
||||||
func DrawText(r Renderer, text string, x, y int, s Style) {
|
func (d draw) Text(r Renderer, text string, x, y int, s Style) {
|
||||||
r.SetFontColor(s.GetFontColor(DefaultTextColor))
|
r.SetFontColor(s.GetFontColor(DefaultTextColor))
|
||||||
r.SetStrokeColor(s.GetStrokeColor())
|
r.SetStrokeColor(s.GetStrokeColor())
|
||||||
r.SetStrokeWidth(s.GetStrokeWidth())
|
r.SetStrokeWidth(s.GetStrokeWidth())
|
||||||
|
@ -244,129 +247,7 @@ func DrawText(r Renderer, text string, x, y int, s Style) {
|
||||||
r.Text(text, x, y)
|
r.Text(text, x, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DrawTextCentered draws text with a given style centered.
|
// TextWithin draws the text within a given box.
|
||||||
func DrawTextCentered(r Renderer, text string, x, y int, s Style) {
|
func (d draw) TextWithin(r Renderer, text string, box Box, s Style) {
|
||||||
r.SetFontColor(s.GetFontColor(DefaultTextColor))
|
|
||||||
r.SetStrokeColor(s.GetStrokeColor())
|
|
||||||
r.SetStrokeWidth(s.GetStrokeWidth())
|
|
||||||
r.SetFont(s.GetFont())
|
|
||||||
r.SetFontSize(s.GetFontSize())
|
|
||||||
|
|
||||||
tb := r.MeasureText(text)
|
|
||||||
tx := x - (tb.Width() >> 1)
|
|
||||||
ty := y - (tb.Height() >> 1)
|
|
||||||
r.Text(text, tx, ty)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateLegend returns a legend renderable function.
|
|
||||||
func CreateLegend(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().IsZero() || s.GetStyle().Show {
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
|
|
||||||
r.SetFont(legendStyle.GetFont())
|
|
||||||
r.SetFontColor(legendStyle.GetFontColor())
|
|
||||||
r.SetFontSize(legendStyle.GetFontSize())
|
|
||||||
|
|
||||||
// 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
|
|
||||||
|
|
||||||
DrawBox(r, legend, legendStyle)
|
|
||||||
|
|
||||||
ycursor := legendContent.Top
|
|
||||||
tx := legendContent.Left
|
|
||||||
legendCount := 0
|
|
||||||
for x := 0; x < len(labels); x++ {
|
|
||||||
if len(labels[x]) > 0 {
|
|
||||||
|
|
||||||
if legendCount > 0 {
|
|
||||||
ycursor += DefaultMinimumTickVerticalSpacing
|
|
||||||
}
|
|
||||||
|
|
||||||
tb := r.MeasureText(labels[x])
|
|
||||||
|
|
||||||
ty := ycursor + tb.Height()
|
|
||||||
r.Text(labels[x], tx, ty)
|
|
||||||
|
|
||||||
th2 := tb.Height() >> 1
|
|
||||||
|
|
||||||
lx := tx + tb.Width() + lineTextGap
|
|
||||||
ly := ty - th2
|
|
||||||
lx2 := legendContent.Right - legendPadding.Right
|
|
||||||
|
|
||||||
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()
|
|
||||||
|
|
||||||
ycursor += tb.Height()
|
|
||||||
legendCount++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright 2010 The draw2d Authors. All rights reserved.
|
|
||||||
// created: 13/12/2010 by Laurent Le Goff
|
|
||||||
|
|
||||||
package drawing
|
package drawing
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -97,5 +97,5 @@ func (ema *EMASeries) ensureCachedValues() {
|
||||||
// Render renders the series.
|
// Render renders the series.
|
||||||
func (ema *EMASeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
|
func (ema *EMASeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
|
||||||
style := ema.Style.InheritFrom(defaults)
|
style := ema.Style.InheritFrom(defaults)
|
||||||
DrawLineSeries(r, canvasBox, xrange, yrange, style, ema)
|
Draw.LineSeries(r, canvasBox, xrange, yrange, style, ema)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,11 +10,13 @@ import (
|
||||||
|
|
||||||
func drawChart(res http.ResponseWriter, req *http.Request) {
|
func drawChart(res http.ResponseWriter, req *http.Request) {
|
||||||
pie := chart.PieChart{
|
pie := chart.PieChart{
|
||||||
|
Title: "test\nchart",
|
||||||
|
TitleStyle: chart.Style{
|
||||||
|
Show: true,
|
||||||
|
FontSize: 32,
|
||||||
|
},
|
||||||
Width: 512,
|
Width: 512,
|
||||||
Height: 512,
|
Height: 512,
|
||||||
Canvas: chart.Style{
|
|
||||||
FillColor: chart.ColorLightGray,
|
|
||||||
},
|
|
||||||
Values: []chart.Value{
|
Values: []chart.Value{
|
||||||
{Value: 5, Label: "Blue"},
|
{Value: 5, Label: "Blue"},
|
||||||
{Value: 5, Label: "Green"},
|
{Value: 5, Label: "Green"},
|
||||||
|
|
|
@ -53,5 +53,5 @@ func (hs HistogramSeries) GetBoundedValue(index int) (x, y1, y2 float64) {
|
||||||
// Render implements Series.Render.
|
// Render implements Series.Render.
|
||||||
func (hs HistogramSeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
|
func (hs HistogramSeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
|
||||||
style := hs.Style.InheritFrom(defaults)
|
style := hs.Style.InheritFrom(defaults)
|
||||||
DrawHistogramSeries(r, canvasBox, xrange, yrange, style, hs)
|
Draw.HistogramSeries(r, canvasBox, xrange, yrange, style, hs)
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 32 KiB |
116
legend.go
Normal file
116
legend.go
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
package chart
|
||||||
|
|
||||||
|
import "github.com/wcharczuk/go-chart/drawing"
|
||||||
|
|
||||||
|
// Legend returns a legend renderable function.
|
||||||
|
func Legend(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().IsZero() || s.GetStyle().Show {
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
|
r.SetFont(legendStyle.GetFont())
|
||||||
|
r.SetFontColor(legendStyle.GetFontColor())
|
||||||
|
r.SetFontSize(legendStyle.GetFontSize())
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
|
||||||
|
ycursor := legendContent.Top
|
||||||
|
tx := legendContent.Left
|
||||||
|
legendCount := 0
|
||||||
|
for x := 0; x < len(labels); x++ {
|
||||||
|
if len(labels[x]) > 0 {
|
||||||
|
|
||||||
|
if legendCount > 0 {
|
||||||
|
ycursor += DefaultMinimumTickVerticalSpacing
|
||||||
|
}
|
||||||
|
|
||||||
|
tb := r.MeasureText(labels[x])
|
||||||
|
|
||||||
|
ty := ycursor + tb.Height()
|
||||||
|
r.Text(labels[x], tx, ty)
|
||||||
|
|
||||||
|
th2 := tb.Height() >> 1
|
||||||
|
|
||||||
|
lx := tx + tb.Width() + lineTextGap
|
||||||
|
ly := ty - th2
|
||||||
|
lx2 := legendContent.Right - legendPadding.Right
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
ycursor += tb.Height()
|
||||||
|
legendCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -128,5 +128,5 @@ func (lrs *LinearRegressionSeries) computeCoefficients() {
|
||||||
// Render renders the series.
|
// Render renders the series.
|
||||||
func (lrs *LinearRegressionSeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
|
func (lrs *LinearRegressionSeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
|
||||||
style := lrs.Style.InheritFrom(defaults)
|
style := lrs.Style.InheritFrom(defaults)
|
||||||
DrawLineSeries(r, canvasBox, xrange, yrange, style, lrs)
|
Draw.LineSeries(r, canvasBox, xrange, yrange, style, lrs)
|
||||||
}
|
}
|
||||||
|
|
|
@ -193,7 +193,7 @@ func (macds *MACDSignalSeries) ensureSignal() {
|
||||||
// Render renders the series.
|
// Render renders the series.
|
||||||
func (macds *MACDSignalSeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
|
func (macds *MACDSignalSeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
|
||||||
style := macds.Style.InheritFrom(defaults)
|
style := macds.Style.InheritFrom(defaults)
|
||||||
DrawLineSeries(r, canvasBox, xrange, yrange, style, macds)
|
Draw.LineSeries(r, canvasBox, xrange, yrange, style, macds)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MACDLineSeries is a series that computes the inner ema1-ema2 value as a series.
|
// MACDLineSeries is a series that computes the inner ema1-ema2 value as a series.
|
||||||
|
@ -285,5 +285,5 @@ func (macdl *MACDLineSeries) ensureEMASeries() {
|
||||||
// Render renders the series.
|
// Render renders the series.
|
||||||
func (macdl *MACDLineSeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
|
func (macdl *MACDLineSeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
|
||||||
style := macdl.Style.InheritFrom(defaults)
|
style := macdl.Style.InheritFrom(defaults)
|
||||||
DrawLineSeries(r, canvasBox, xrange, yrange, style, macdl)
|
Draw.LineSeries(r, canvasBox, xrange, yrange, style, macdl)
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,14 +99,14 @@ func (pc PieChart) Render(rp RendererProvider, w io.Writer) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pc PieChart) drawBackground(r Renderer) {
|
func (pc PieChart) drawBackground(r Renderer) {
|
||||||
DrawBox(r, Box{
|
Draw.Box(r, Box{
|
||||||
Right: pc.GetWidth(),
|
Right: pc.GetWidth(),
|
||||||
Bottom: pc.GetHeight(),
|
Bottom: pc.GetHeight(),
|
||||||
}, pc.getBackgroundStyle())
|
}, pc.getBackgroundStyle())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pc PieChart) drawCanvas(r Renderer, canvasBox Box) {
|
func (pc PieChart) drawCanvas(r Renderer, canvasBox Box) {
|
||||||
DrawBox(r, canvasBox, pc.getCanvasStyle())
|
Draw.Box(r, canvasBox, pc.getCanvasStyle())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pc PieChart) drawTitle(r Renderer) {
|
func (pc PieChart) drawTitle(r Renderer) {
|
||||||
|
@ -138,7 +138,7 @@ func (pc PieChart) drawSlices(r Renderer, canvasBox Box, values []Value) {
|
||||||
var rads, delta, delta2, total float64
|
var rads, delta, delta2, total float64
|
||||||
var lx, ly int
|
var lx, ly int
|
||||||
for index, v := range values {
|
for index, v := range values {
|
||||||
v.Style.InheritFrom(pc.stylePieChartValue(index)).PersistToRenderer(r)
|
v.Style.InheritFrom(pc.stylePieChartValue(index)).WriteToRenderer(r)
|
||||||
|
|
||||||
r.MoveTo(cx, cy)
|
r.MoveTo(cx, cy)
|
||||||
rads = PercentToRadians(total)
|
rads = PercentToRadians(total)
|
||||||
|
@ -155,7 +155,7 @@ func (pc PieChart) drawSlices(r Renderer, canvasBox Box, values []Value) {
|
||||||
// draw the labels
|
// draw the labels
|
||||||
total = 0
|
total = 0
|
||||||
for index, v := range values {
|
for index, v := range values {
|
||||||
v.Style.InheritFrom(pc.stylePieChartValue(index)).PersistToRenderer(r)
|
v.Style.InheritFrom(pc.stylePieChartValue(index)).WriteToRenderer(r)
|
||||||
if len(v.Label) > 0 {
|
if len(v.Label) > 0 {
|
||||||
delta2 = PercentToRadians(total + (v.Value / 2.0))
|
delta2 = PercentToRadians(total + (v.Value / 2.0))
|
||||||
delta2 = RadianAdd(delta2, _pi2)
|
delta2 = RadianAdd(delta2, _pi2)
|
||||||
|
|
|
@ -86,5 +86,5 @@ func (sma SMASeries) getAverage(index int) float64 {
|
||||||
// Render renders the series.
|
// Render renders the series.
|
||||||
func (sma SMASeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
|
func (sma SMASeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
|
||||||
style := sma.Style.InheritFrom(defaults)
|
style := sma.Style.InheritFrom(defaults)
|
||||||
DrawLineSeries(r, canvasBox, xrange, yrange, style, sma)
|
Draw.LineSeries(r, canvasBox, xrange, yrange, style, sma)
|
||||||
}
|
}
|
||||||
|
|
|
@ -134,7 +134,7 @@ func (sbc StackedBarChart) drawBar(r Renderer, canvasBox Box, xoffset int, bar S
|
||||||
for index, bv := range normalizedBarComponents {
|
for index, bv := range normalizedBarComponents {
|
||||||
barHeight := int(bv.Value * float64(canvasBox.Height()))
|
barHeight := int(bv.Value * float64(canvasBox.Height()))
|
||||||
barBox := Box{Top: yoffset, Left: bxl, Right: bxr, Bottom: yoffset + barHeight}
|
barBox := Box{Top: yoffset, Left: bxl, Right: bxr, Bottom: yoffset + barHeight}
|
||||||
DrawBox(r, barBox, bv.Style.InheritFrom(sbc.styleDefaultsStackedBarValue(index)))
|
Draw.Box(r, barBox, bv.Style.InheritFrom(sbc.styleDefaultsStackedBarValue(index)))
|
||||||
yoffset += barHeight
|
yoffset += barHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
143
style.go
143
style.go
|
@ -21,6 +21,10 @@ type Style struct {
|
||||||
FontSize float64
|
FontSize float64
|
||||||
FontColor drawing.Color
|
FontColor drawing.Color
|
||||||
Font *truetype.Font
|
Font *truetype.Font
|
||||||
|
|
||||||
|
TextHorizontalAlign textHorizontalAlign
|
||||||
|
TextVerticalAlign textVerticalAlign
|
||||||
|
TextWrap textWrap
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsZero returns if the object is set or not.
|
// IsZero returns if the object is set or not.
|
||||||
|
@ -185,8 +189,41 @@ func (s Style) GetPadding(defaults ...Box) Box {
|
||||||
return s.Padding
|
return s.Padding
|
||||||
}
|
}
|
||||||
|
|
||||||
// PersistToRenderer passes the style onto a renderer.
|
// GetTextHorizontalAlign returns the horizontal alignment.
|
||||||
func (s Style) PersistToRenderer(r Renderer) {
|
func (s Style) GetTextHorizontalAlign(defaults ...textHorizontalAlign) textHorizontalAlign {
|
||||||
|
if s.TextHorizontalAlign == TextHorizontalAlignUnset {
|
||||||
|
if len(defaults) > 0 {
|
||||||
|
return defaults[0]
|
||||||
|
}
|
||||||
|
return TextHorizontalAlignLeft
|
||||||
|
}
|
||||||
|
return s.TextHorizontalAlign
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTextVerticalAlign returns the vertical alignment.
|
||||||
|
func (s Style) GetTextVerticalAlign(defaults ...textVerticalAlign) textVerticalAlign {
|
||||||
|
if s.TextVerticalAlign == TextVerticalAlignUnset {
|
||||||
|
if len(defaults) > 0 {
|
||||||
|
return defaults[0]
|
||||||
|
}
|
||||||
|
return TextVerticalAlignBaseline
|
||||||
|
}
|
||||||
|
return s.TextVerticalAlign
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTextWrap returns the word wrap.
|
||||||
|
func (s Style) GetTextWrap(defaults ...textWrap) textWrap {
|
||||||
|
if s.TextWrap == TextWrapUnset {
|
||||||
|
if len(defaults) > 0 {
|
||||||
|
return defaults[0]
|
||||||
|
}
|
||||||
|
return TextWrapWord
|
||||||
|
}
|
||||||
|
return s.TextWrap
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteToRenderer passes the style's options to a renderer.
|
||||||
|
func (s Style) WriteToRenderer(r Renderer) {
|
||||||
r.SetStrokeColor(s.GetStrokeColor())
|
r.SetStrokeColor(s.GetStrokeColor())
|
||||||
r.SetStrokeWidth(s.GetStrokeWidth())
|
r.SetStrokeWidth(s.GetStrokeWidth())
|
||||||
r.SetStrokeDashArray(s.GetStrokeDashArray())
|
r.SetStrokeDashArray(s.GetStrokeDashArray())
|
||||||
|
@ -196,6 +233,21 @@ func (s Style) PersistToRenderer(r Renderer) {
|
||||||
r.SetFontSize(s.GetFontSize())
|
r.SetFontSize(s.GetFontSize())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteDrawingOptionsToRenderer passes just the drawing style options to a renderer.
|
||||||
|
func (s Style) WriteDrawingOptionsToRenderer(r Renderer) {
|
||||||
|
r.SetStrokeColor(s.GetStrokeColor())
|
||||||
|
r.SetStrokeWidth(s.GetStrokeWidth())
|
||||||
|
r.SetStrokeDashArray(s.GetStrokeDashArray())
|
||||||
|
r.SetFillColor(s.GetFillColor())
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteTextOptionsToRenderer passes just the text style options to a renderer.
|
||||||
|
func (s Style) WriteTextOptionsToRenderer(r Renderer) {
|
||||||
|
r.SetFont(s.GetFont())
|
||||||
|
r.SetFontColor(s.GetFontColor())
|
||||||
|
r.SetFontSize(s.GetFontSize())
|
||||||
|
}
|
||||||
|
|
||||||
// InheritFrom coalesces two styles into a new style.
|
// InheritFrom coalesces two styles into a new style.
|
||||||
func (s Style) InheritFrom(defaults Style) (final Style) {
|
func (s Style) InheritFrom(defaults Style) (final Style) {
|
||||||
final.StrokeColor = s.GetStrokeColor(defaults.StrokeColor)
|
final.StrokeColor = s.GetStrokeColor(defaults.StrokeColor)
|
||||||
|
@ -206,47 +258,14 @@ func (s Style) InheritFrom(defaults Style) (final Style) {
|
||||||
final.FontSize = s.GetFontSize(defaults.FontSize)
|
final.FontSize = s.GetFontSize(defaults.FontSize)
|
||||||
final.Font = s.GetFont(defaults.Font)
|
final.Font = s.GetFont(defaults.Font)
|
||||||
final.Padding = s.GetPadding(defaults.Padding)
|
final.Padding = s.GetPadding(defaults.Padding)
|
||||||
|
final.TextHorizontalAlign = s.GetTextHorizontalAlign(defaults.TextHorizontalAlign)
|
||||||
|
final.TextVerticalAlign = s.GetTextVerticalAlign(defaults.TextVerticalAlign)
|
||||||
|
final.TextWrap = s.GetTextWrap(defaults.TextWrap)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// SVG returns the style as a svg style string.
|
// GetStrokeOptions returns the stroke components.
|
||||||
func (s Style) SVG(dpi float64) string {
|
func (s Style) GetStrokeOptions() Style {
|
||||||
sw := s.StrokeWidth
|
|
||||||
sc := s.StrokeColor
|
|
||||||
fc := s.FillColor
|
|
||||||
fs := s.FontSize
|
|
||||||
fnc := s.FontColor
|
|
||||||
|
|
||||||
strokeWidthText := "stroke-width:0"
|
|
||||||
if sw != 0 {
|
|
||||||
strokeWidthText = "stroke-width:" + fmt.Sprintf("%d", int(sw))
|
|
||||||
}
|
|
||||||
|
|
||||||
strokeText := "stroke:none"
|
|
||||||
if !sc.IsZero() {
|
|
||||||
strokeText = "stroke:" + sc.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
fillText := "fill:none"
|
|
||||||
if !fc.IsZero() {
|
|
||||||
fillText = "fill:" + fc.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
fontSizeText := ""
|
|
||||||
if fs != 0 {
|
|
||||||
fontSizeText = "font-size:" + fmt.Sprintf("%.1fpx", drawing.PointsToPixels(dpi, fs))
|
|
||||||
}
|
|
||||||
|
|
||||||
if !fnc.IsZero() {
|
|
||||||
fillText = "fill:" + fnc.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
fontText := s.SVGFontFace()
|
|
||||||
return strings.Join([]string{strokeWidthText, strokeText, fillText, fontSizeText, fontText}, ";")
|
|
||||||
}
|
|
||||||
|
|
||||||
// SVGStroke returns the stroke components.
|
|
||||||
func (s Style) SVGStroke() Style {
|
|
||||||
return Style{
|
return Style{
|
||||||
StrokeDashArray: s.StrokeDashArray,
|
StrokeDashArray: s.StrokeDashArray,
|
||||||
StrokeColor: s.StrokeColor,
|
StrokeColor: s.StrokeColor,
|
||||||
|
@ -254,15 +273,15 @@ func (s Style) SVGStroke() Style {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SVGFill returns the fill components.
|
// GetFillOptions returns the fill components.
|
||||||
func (s Style) SVGFill() Style {
|
func (s Style) GetFillOptions() Style {
|
||||||
return Style{
|
return Style{
|
||||||
FillColor: s.FillColor,
|
FillColor: s.FillColor,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SVGFillAndStroke returns the fill and stroke components.
|
// GetFillAndStrokeOptions returns the fill and stroke components.
|
||||||
func (s Style) SVGFillAndStroke() Style {
|
func (s Style) GetFillAndStrokeOptions() Style {
|
||||||
return Style{
|
return Style{
|
||||||
StrokeDashArray: s.StrokeDashArray,
|
StrokeDashArray: s.StrokeDashArray,
|
||||||
FillColor: s.FillColor,
|
FillColor: s.FillColor,
|
||||||
|
@ -271,34 +290,14 @@ func (s Style) SVGFillAndStroke() Style {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SVGText returns just the text components of the style.
|
// GetTextOptions returns just the text components of the style.
|
||||||
func (s Style) SVGText() Style {
|
func (s Style) GetTextOptions() Style {
|
||||||
return Style{
|
return Style{
|
||||||
FontColor: s.FontColor,
|
FontColor: s.FontColor,
|
||||||
FontSize: s.FontSize,
|
FontSize: s.FontSize,
|
||||||
|
Font: s.Font,
|
||||||
|
TextHorizontalAlign: s.TextHorizontalAlign,
|
||||||
|
TextVerticalAlign: s.TextVerticalAlign,
|
||||||
|
TextWrap: s.TextWrap,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SVGFontFace returns the font face for the style.
|
|
||||||
func (s Style) SVGFontFace() string {
|
|
||||||
family := "sans-serif"
|
|
||||||
if s.GetFont() != nil {
|
|
||||||
name := s.GetFont().Name(truetype.NameIDFontFamily)
|
|
||||||
if len(name) != 0 {
|
|
||||||
family = fmt.Sprintf(`'%s',%s`, name, family)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("font-family:%s", family)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SVGStrokeDashArray returns the stroke-dasharray property of a style.
|
|
||||||
func (s Style) SVGStrokeDashArray() string {
|
|
||||||
if len(s.StrokeDashArray) > 0 {
|
|
||||||
var values []string
|
|
||||||
for _, v := range s.StrokeDashArray {
|
|
||||||
values = append(values, fmt.Sprintf("%0.1f", v))
|
|
||||||
}
|
|
||||||
return "stroke-dasharray=\"" + strings.Join(values, ", ") + "\""
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package chart
|
package chart
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/blendlabs/go-assert"
|
"github.com/blendlabs/go-assert"
|
||||||
|
@ -146,29 +145,7 @@ func TestStyleWithDefaultsFrom(t *testing.T) {
|
||||||
assert.Equal(set, coalesced)
|
assert.Equal(set, coalesced)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStyleSVG(t *testing.T) {
|
func TestStyleGetStrokeOptions(t *testing.T) {
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
f, err := GetDefaultFont()
|
|
||||||
assert.Nil(err)
|
|
||||||
|
|
||||||
set := Style{
|
|
||||||
StrokeColor: drawing.ColorWhite,
|
|
||||||
StrokeWidth: 5.0,
|
|
||||||
FillColor: drawing.ColorWhite,
|
|
||||||
FontColor: drawing.ColorWhite,
|
|
||||||
Font: f,
|
|
||||||
Padding: DefaultBackgroundPadding,
|
|
||||||
}
|
|
||||||
|
|
||||||
svgString := set.SVG(DefaultDPI)
|
|
||||||
assert.NotEmpty(svgString)
|
|
||||||
assert.True(strings.Contains(svgString, "stroke:rgba(255,255,255,1.0)"))
|
|
||||||
assert.True(strings.Contains(svgString, "stroke-width:5"))
|
|
||||||
assert.True(strings.Contains(svgString, "fill:rgba(255,255,255,1.0)"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStyleSVGStroke(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
set := Style{
|
set := Style{
|
||||||
|
@ -178,14 +155,14 @@ func TestStyleSVGStroke(t *testing.T) {
|
||||||
FontColor: drawing.ColorWhite,
|
FontColor: drawing.ColorWhite,
|
||||||
Padding: DefaultBackgroundPadding,
|
Padding: DefaultBackgroundPadding,
|
||||||
}
|
}
|
||||||
svgStroke := set.SVGStroke()
|
svgStroke := set.GetStrokeOptions()
|
||||||
assert.False(svgStroke.StrokeColor.IsZero())
|
assert.False(svgStroke.StrokeColor.IsZero())
|
||||||
assert.NotZero(svgStroke.StrokeWidth)
|
assert.NotZero(svgStroke.StrokeWidth)
|
||||||
assert.True(svgStroke.FillColor.IsZero())
|
assert.True(svgStroke.FillColor.IsZero())
|
||||||
assert.True(svgStroke.FontColor.IsZero())
|
assert.True(svgStroke.FontColor.IsZero())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStyleSVGFill(t *testing.T) {
|
func TestStyleGetFillOptions(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
set := Style{
|
set := Style{
|
||||||
|
@ -195,14 +172,14 @@ func TestStyleSVGFill(t *testing.T) {
|
||||||
FontColor: drawing.ColorWhite,
|
FontColor: drawing.ColorWhite,
|
||||||
Padding: DefaultBackgroundPadding,
|
Padding: DefaultBackgroundPadding,
|
||||||
}
|
}
|
||||||
svgFill := set.SVGFill()
|
svgFill := set.GetFillOptions()
|
||||||
assert.False(svgFill.FillColor.IsZero())
|
assert.False(svgFill.FillColor.IsZero())
|
||||||
assert.Zero(svgFill.StrokeWidth)
|
assert.Zero(svgFill.StrokeWidth)
|
||||||
assert.True(svgFill.StrokeColor.IsZero())
|
assert.True(svgFill.StrokeColor.IsZero())
|
||||||
assert.True(svgFill.FontColor.IsZero())
|
assert.True(svgFill.FontColor.IsZero())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStyleSVGFillAndStroke(t *testing.T) {
|
func TestStyleGetFillAndStrokeOptions(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
set := Style{
|
set := Style{
|
||||||
|
@ -212,14 +189,14 @@ func TestStyleSVGFillAndStroke(t *testing.T) {
|
||||||
FontColor: drawing.ColorWhite,
|
FontColor: drawing.ColorWhite,
|
||||||
Padding: DefaultBackgroundPadding,
|
Padding: DefaultBackgroundPadding,
|
||||||
}
|
}
|
||||||
svgFillAndStroke := set.SVGFillAndStroke()
|
svgFillAndStroke := set.GetFillAndStrokeOptions()
|
||||||
assert.False(svgFillAndStroke.FillColor.IsZero())
|
assert.False(svgFillAndStroke.FillColor.IsZero())
|
||||||
assert.NotZero(svgFillAndStroke.StrokeWidth)
|
assert.NotZero(svgFillAndStroke.StrokeWidth)
|
||||||
assert.False(svgFillAndStroke.StrokeColor.IsZero())
|
assert.False(svgFillAndStroke.StrokeColor.IsZero())
|
||||||
assert.True(svgFillAndStroke.FontColor.IsZero())
|
assert.True(svgFillAndStroke.FontColor.IsZero())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStyleSVGText(t *testing.T) {
|
func TestStyleGetTextOptions(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
set := Style{
|
set := Style{
|
||||||
|
@ -229,7 +206,7 @@ func TestStyleSVGText(t *testing.T) {
|
||||||
FontColor: drawing.ColorWhite,
|
FontColor: drawing.ColorWhite,
|
||||||
Padding: DefaultBackgroundPadding,
|
Padding: DefaultBackgroundPadding,
|
||||||
}
|
}
|
||||||
svgStroke := set.SVGText()
|
svgStroke := set.GetTextOptions()
|
||||||
assert.True(svgStroke.StrokeColor.IsZero())
|
assert.True(svgStroke.StrokeColor.IsZero())
|
||||||
assert.Zero(svgStroke.StrokeWidth)
|
assert.Zero(svgStroke.StrokeWidth)
|
||||||
assert.True(svgStroke.FillColor.IsZero())
|
assert.True(svgStroke.FillColor.IsZero())
|
||||||
|
|
153
text.go
Normal file
153
text.go
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
package chart
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
// TextHorizontalAlign is an enum for the horizontal alignment options.
|
||||||
|
type textHorizontalAlign int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// TextHorizontalAlignUnset is the unset state for text horizontal alignment.
|
||||||
|
TextHorizontalAlignUnset textHorizontalAlign = 0
|
||||||
|
// TextHorizontalAlignLeft aligns a string horizontally so that it's left ligature starts at horizontal pixel 0.
|
||||||
|
TextHorizontalAlignLeft textHorizontalAlign = 1
|
||||||
|
// TextHorizontalAlignCenter left aligns a string horizontally so that there are equal pixels
|
||||||
|
// to the left and to the right of a string within a box.
|
||||||
|
TextHorizontalAlignCenter textHorizontalAlign = 2
|
||||||
|
// TextHorizontalAlignRight right aligns a string horizontally so that the right ligature ends at the right-most pixel
|
||||||
|
// of a box.
|
||||||
|
TextHorizontalAlignRight textHorizontalAlign = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
// TextWrap is an enum for the word wrap options.
|
||||||
|
type textWrap int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// TextWrapUnset is the unset state for text wrap options.
|
||||||
|
TextWrapUnset textWrap = 0
|
||||||
|
// TextWrapNone will spill text past horizontal boundaries.
|
||||||
|
TextWrapNone textWrap = 1
|
||||||
|
// TextWrapWord will split a string on words (i.e. spaces) to fit within a horizontal boundary.
|
||||||
|
TextWrapWord textWrap = 2
|
||||||
|
// TextWrapRune will split a string on a rune (i.e. utf-8 codepage) to fit within a horizontal boundary.
|
||||||
|
TextWrapRune textWrap = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
// TextVerticalAlign is an enum for the vertical alignment options.
|
||||||
|
type textVerticalAlign int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// TextVerticalAlignUnset is the unset state for vertical alignment options.
|
||||||
|
TextVerticalAlignUnset textVerticalAlign = 0
|
||||||
|
// TextVerticalAlignBaseline aligns text according to the "baseline" of the string, or where a normal ascender begins.
|
||||||
|
TextVerticalAlignBaseline textVerticalAlign = 1
|
||||||
|
// TextVerticalAlignBottom aligns the text according to the lowers pixel of any of the ligatures (ex. g or q both extend below the baseline).
|
||||||
|
TextVerticalAlignBottom textVerticalAlign = 2
|
||||||
|
// TextVerticalAlignMiddle aligns the text so that there is an equal amount of space above and below the top and bottom of the ligatures.
|
||||||
|
TextVerticalAlignMiddle textVerticalAlign = 3
|
||||||
|
// TextVerticalAlignMiddleBaseline aligns the text veritcally so that there is an equal number of pixels above and below the baseline of the string.
|
||||||
|
TextVerticalAlignMiddleBaseline textVerticalAlign = 4
|
||||||
|
// TextVerticalAlignTop alignts the text so that the top of the ligatures are at y-pixel 0 in the container.
|
||||||
|
TextVerticalAlignTop textVerticalAlign = 5
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Text contains utilities for text.
|
||||||
|
Text = &text{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// TextStyle encapsulates text style options.
|
||||||
|
type TextStyle struct {
|
||||||
|
HorizontalAlign textHorizontalAlign
|
||||||
|
VerticalAlign textVerticalAlign
|
||||||
|
Wrap textWrap
|
||||||
|
}
|
||||||
|
|
||||||
|
type text struct{}
|
||||||
|
|
||||||
|
func (t text) WrapFit(r Renderer, value string, width int, style Style, wrapOption textWrap) []string {
|
||||||
|
valueBox := r.MeasureText(value)
|
||||||
|
if valueBox.Width() > width {
|
||||||
|
switch wrapOption {
|
||||||
|
case TextWrapRune:
|
||||||
|
return t.WrapFitRune(r, value, width, style)
|
||||||
|
case TextWrapWord:
|
||||||
|
return t.WrapFitWord(r, value, width, style)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return []string{value}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t text) WrapFitWord(r Renderer, value string, width int, style Style) []string {
|
||||||
|
style.WriteToRenderer(r)
|
||||||
|
|
||||||
|
var output []string
|
||||||
|
var line string
|
||||||
|
var word string
|
||||||
|
|
||||||
|
var textBox Box
|
||||||
|
|
||||||
|
for _, c := range value {
|
||||||
|
if c == rune('\n') { // commit the line to output
|
||||||
|
output = append(output, t.Trim(line+word))
|
||||||
|
line = ""
|
||||||
|
word = ""
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
textBox = r.MeasureText(line + word + string(c))
|
||||||
|
|
||||||
|
if textBox.Width() >= width {
|
||||||
|
output = append(output, t.Trim(line))
|
||||||
|
line = word
|
||||||
|
word = string(c)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if c == rune(' ') || c == rune('\t') {
|
||||||
|
line = line + word + string(c)
|
||||||
|
word = ""
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
word = word + string(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
return append(output, t.Trim(line+word))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t text) WrapFitRune(r Renderer, value string, width int, style Style) []string {
|
||||||
|
style.WriteToRenderer(r)
|
||||||
|
|
||||||
|
var output []string
|
||||||
|
var line string
|
||||||
|
var textBox Box
|
||||||
|
for _, c := range value {
|
||||||
|
if c == rune('\n') {
|
||||||
|
output = append(output, line)
|
||||||
|
line = ""
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
textBox = r.MeasureText(line + string(c))
|
||||||
|
|
||||||
|
if textBox.Width() >= width {
|
||||||
|
output = append(output, line)
|
||||||
|
line = string(c)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
line = line + string(c)
|
||||||
|
}
|
||||||
|
return t.appendLast(output, line)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t text) Trim(value string) string {
|
||||||
|
return strings.Trim(value, " \t\n\r")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t text) appendLast(lines []string, text string) []string {
|
||||||
|
if len(lines) == 0 {
|
||||||
|
return []string{text}
|
||||||
|
}
|
||||||
|
lastLine := lines[len(lines)-1]
|
||||||
|
lines[len(lines)-1] = lastLine + text
|
||||||
|
return lines
|
||||||
|
}
|
32
text_test.go
Normal file
32
text_test.go
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
package chart
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
assert "github.com/blendlabs/go-assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTextWrapWord(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
r, err := PNG(1024, 1024)
|
||||||
|
assert.Nil(err)
|
||||||
|
f, err := GetDefaultFont()
|
||||||
|
assert.Nil(err)
|
||||||
|
|
||||||
|
basicTextStyle := Style{Font: f, FontSize: 24}
|
||||||
|
|
||||||
|
output := Text.WrapFitWord(r, "this is a test string", 100, basicTextStyle)
|
||||||
|
assert.NotEmpty(output)
|
||||||
|
assert.Len(output, 3)
|
||||||
|
|
||||||
|
for _, line := range output {
|
||||||
|
basicTextStyle.WriteToRenderer(r)
|
||||||
|
lineBox := r.MeasureText(line)
|
||||||
|
assert.True(lineBox.Width() < 100, line+": "+lineBox.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
output = Text.WrapFitWord(r, "foo", 100, basicTextStyle)
|
||||||
|
assert.Len(output, 1)
|
||||||
|
assert.Equal("foo", output[0])
|
||||||
|
}
|
|
@ -57,5 +57,5 @@ func (ts TimeSeries) GetYAxis() YAxisType {
|
||||||
// Render renders the series.
|
// Render renders the series.
|
||||||
func (ts TimeSeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
|
func (ts TimeSeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
|
||||||
style := ts.Style.InheritFrom(defaults)
|
style := ts.Style.InheritFrom(defaults)
|
||||||
DrawLineSeries(r, canvasBox, xrange, yrange, style, ts)
|
Draw.LineSeries(r, canvasBox, xrange, yrange, style, ts)
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,30 +110,28 @@ func (vr *vectorRenderer) Close() {
|
||||||
|
|
||||||
// Stroke draws the path with no fill.
|
// Stroke draws the path with no fill.
|
||||||
func (vr *vectorRenderer) Stroke() {
|
func (vr *vectorRenderer) Stroke() {
|
||||||
vr.drawPath(vr.s.SVGStroke())
|
vr.drawPath(vr.s.GetStrokeOptions())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fill draws the path with no stroke.
|
// Fill draws the path with no stroke.
|
||||||
func (vr *vectorRenderer) Fill() {
|
func (vr *vectorRenderer) Fill() {
|
||||||
vr.drawPath(vr.s.SVGFill())
|
vr.drawPath(vr.s.GetFillOptions())
|
||||||
}
|
}
|
||||||
|
|
||||||
// FillStroke draws the path with both fill and stroke.
|
// FillStroke draws the path with both fill and stroke.
|
||||||
func (vr *vectorRenderer) FillStroke() {
|
func (vr *vectorRenderer) FillStroke() {
|
||||||
s := vr.s.SVGFillAndStroke()
|
vr.drawPath(vr.s.GetFillAndStrokeOptions())
|
||||||
vr.drawPath(s)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// drawPath draws a path.
|
// drawPath draws a path.
|
||||||
func (vr *vectorRenderer) drawPath(s Style) {
|
func (vr *vectorRenderer) drawPath(s Style) {
|
||||||
vr.c.Path(strings.Join(vr.p, "\n"), &s)
|
vr.c.Path(strings.Join(vr.p, "\n"), vr.s.GetFillAndStrokeOptions())
|
||||||
vr.p = []string{} // clear the path
|
vr.p = []string{} // clear the path
|
||||||
}
|
}
|
||||||
|
|
||||||
// Circle implements the interface method.
|
// Circle implements the interface method.
|
||||||
func (vr *vectorRenderer) Circle(radius float64, x, y int) {
|
func (vr *vectorRenderer) Circle(radius float64, x, y int) {
|
||||||
style := vr.s.SVGFillAndStroke()
|
vr.c.Circle(x, y, int(radius), vr.s.GetFillAndStrokeOptions())
|
||||||
vr.c.Circle(x, y, int(radius), &style)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetFont implements the interface method.
|
// SetFont implements the interface method.
|
||||||
|
@ -153,8 +151,7 @@ func (vr *vectorRenderer) SetFontSize(size float64) {
|
||||||
|
|
||||||
// Text draws a text blob.
|
// Text draws a text blob.
|
||||||
func (vr *vectorRenderer) Text(body string, x, y int) {
|
func (vr *vectorRenderer) Text(body string, x, y int) {
|
||||||
style := vr.s.SVGText()
|
vr.c.Text(x, y, body, vr.s.GetTextOptions())
|
||||||
vr.c.Text(x, y, body, &style)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MeasureText uses the truetype font drawer to measure the width of text.
|
// MeasureText uses the truetype font drawer to measure the width of text.
|
||||||
|
@ -200,22 +197,82 @@ func (c *canvas) Start(width, height int) {
|
||||||
c.w.Write([]byte(fmt.Sprintf(`<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="%d" height="%d">\n`, c.width, c.height)))
|
c.w.Write([]byte(fmt.Sprintf(`<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="%d" height="%d">\n`, c.width, c.height)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *canvas) Path(d string, style *Style) {
|
func (c *canvas) Path(d string, style Style) {
|
||||||
var strokeDashArrayProperty string
|
var strokeDashArrayProperty string
|
||||||
if len(style.StrokeDashArray) > 0 {
|
if len(style.StrokeDashArray) > 0 {
|
||||||
strokeDashArrayProperty = style.SVGStrokeDashArray()
|
strokeDashArrayProperty = c.getStrokeDashArray(style)
|
||||||
}
|
}
|
||||||
c.w.Write([]byte(fmt.Sprintf(`<path %s d="%s" style="%s"/>\n`, strokeDashArrayProperty, d, style.SVG(c.dpi))))
|
c.w.Write([]byte(fmt.Sprintf(`<path %s d="%s" style="%s"/>\n`, strokeDashArrayProperty, d, c.styleAsSVG(style))))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *canvas) Text(x, y int, body string, style *Style) {
|
func (c *canvas) Text(x, y int, body string, style Style) {
|
||||||
c.w.Write([]byte(fmt.Sprintf(`<text x="%d" y="%d" style="%s">%s</text>`, x, y, style.SVG(c.dpi), body)))
|
c.w.Write([]byte(fmt.Sprintf(`<text x="%d" y="%d" style="%s">%s</text>`, x, y, c.styleAsSVG(style), body)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *canvas) Circle(x, y, r int, style *Style) {
|
func (c *canvas) Circle(x, y, r int, style Style) {
|
||||||
c.w.Write([]byte(fmt.Sprintf(`<circle cx="%d" cy="%d" r="%d" style="%s">`, x, y, r, style.SVG(c.dpi))))
|
c.w.Write([]byte(fmt.Sprintf(`<circle cx="%d" cy="%d" r="%d" style="%s">`, x, y, r, c.styleAsSVG(style))))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *canvas) End() {
|
func (c *canvas) End() {
|
||||||
c.w.Write([]byte("</svg>"))
|
c.w.Write([]byte("</svg>"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getStrokeDashArray returns the stroke-dasharray property of a style.
|
||||||
|
func (c *canvas) getStrokeDashArray(s Style) string {
|
||||||
|
if len(s.StrokeDashArray) > 0 {
|
||||||
|
var values []string
|
||||||
|
for _, v := range s.StrokeDashArray {
|
||||||
|
values = append(values, fmt.Sprintf("%0.1f", v))
|
||||||
|
}
|
||||||
|
return "stroke-dasharray=\"" + strings.Join(values, ", ") + "\""
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFontFace returns the font face for the style.
|
||||||
|
func (c *canvas) getFontFace(s Style) string {
|
||||||
|
family := "sans-serif"
|
||||||
|
if s.GetFont() != nil {
|
||||||
|
name := s.GetFont().Name(truetype.NameIDFontFamily)
|
||||||
|
if len(name) != 0 {
|
||||||
|
family = fmt.Sprintf(`'%s',%s`, name, family)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("font-family:%s", family)
|
||||||
|
}
|
||||||
|
|
||||||
|
// styleAsSVG returns the style as a svg style string.
|
||||||
|
func (c *canvas) styleAsSVG(s Style) string {
|
||||||
|
sw := s.StrokeWidth
|
||||||
|
sc := s.StrokeColor
|
||||||
|
fc := s.FillColor
|
||||||
|
fs := s.FontSize
|
||||||
|
fnc := s.FontColor
|
||||||
|
|
||||||
|
strokeWidthText := "stroke-width:0"
|
||||||
|
if sw != 0 {
|
||||||
|
strokeWidthText = "stroke-width:" + fmt.Sprintf("%d", int(sw))
|
||||||
|
}
|
||||||
|
|
||||||
|
strokeText := "stroke:none"
|
||||||
|
if !sc.IsZero() {
|
||||||
|
strokeText = "stroke:" + sc.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
fillText := "fill:none"
|
||||||
|
if !fc.IsZero() {
|
||||||
|
fillText = "fill:" + fc.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
fontSizeText := ""
|
||||||
|
if fs != 0 {
|
||||||
|
fontSizeText = "font-size:" + fmt.Sprintf("%.1fpx", drawing.PointsToPixels(c.dpi, fs))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !fnc.IsZero() {
|
||||||
|
fillText = "fill:" + fnc.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
fontText := c.getFontFace(s)
|
||||||
|
return strings.Join([]string{strokeWidthText, strokeText, fillText, fontSizeText, fontText}, ";")
|
||||||
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/blendlabs/go-assert"
|
"github.com/blendlabs/go-assert"
|
||||||
|
"github.com/wcharczuk/go-chart/drawing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestVectorRendererPath(t *testing.T) {
|
func TestVectorRendererPath(t *testing.T) {
|
||||||
|
@ -50,3 +51,27 @@ func TestVectorRendererMeasureText(t *testing.T) {
|
||||||
assert.Equal(21, tb.Width())
|
assert.Equal(21, tb.Width())
|
||||||
assert.Equal(15, tb.Height())
|
assert.Equal(15, tb.Height())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCanvasStyleSVG(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
f, err := GetDefaultFont()
|
||||||
|
assert.Nil(err)
|
||||||
|
|
||||||
|
set := Style{
|
||||||
|
StrokeColor: drawing.ColorWhite,
|
||||||
|
StrokeWidth: 5.0,
|
||||||
|
FillColor: drawing.ColorWhite,
|
||||||
|
FontColor: drawing.ColorWhite,
|
||||||
|
Font: f,
|
||||||
|
Padding: DefaultBackgroundPadding,
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas := &canvas{dpi: DefaultDPI}
|
||||||
|
|
||||||
|
svgString := canvas.styleAsSVG(set)
|
||||||
|
assert.NotEmpty(svgString)
|
||||||
|
assert.True(strings.Contains(svgString, "stroke:rgba(255,255,255,1.0)"))
|
||||||
|
assert.True(strings.Contains(svgString, "stroke-width:5"))
|
||||||
|
assert.True(strings.Contains(svgString, "fill:rgba(255,255,255,1.0)"))
|
||||||
|
}
|
||||||
|
|
4
xaxis.go
4
xaxis.go
|
@ -54,7 +54,7 @@ func (xa XAxis) GetGridLines(ticks []Tick) []GridLine {
|
||||||
|
|
||||||
// Measure returns the bounds of the axis.
|
// Measure returns the bounds of the axis.
|
||||||
func (xa XAxis) Measure(r Renderer, canvasBox Box, ra Range, defaults Style, ticks []Tick) Box {
|
func (xa XAxis) Measure(r Renderer, canvasBox Box, ra Range, defaults Style, ticks []Tick) Box {
|
||||||
xa.Style.InheritFrom(defaults).PersistToRenderer(r)
|
xa.Style.InheritFrom(defaults).WriteToRenderer(r)
|
||||||
sort.Sort(Ticks(ticks))
|
sort.Sort(Ticks(ticks))
|
||||||
|
|
||||||
var left, right, top, bottom = math.MaxInt32, 0, math.MaxInt32, 0
|
var left, right, top, bottom = math.MaxInt32, 0, math.MaxInt32, 0
|
||||||
|
@ -82,7 +82,7 @@ func (xa XAxis) Measure(r Renderer, canvasBox Box, ra Range, defaults Style, tic
|
||||||
|
|
||||||
// Render renders the axis
|
// Render renders the axis
|
||||||
func (xa XAxis) Render(r Renderer, canvasBox Box, ra Range, defaults Style, ticks []Tick) {
|
func (xa XAxis) Render(r Renderer, canvasBox Box, ra Range, defaults Style, ticks []Tick) {
|
||||||
xa.Style.InheritFrom(defaults).PersistToRenderer(r)
|
xa.Style.InheritFrom(defaults).WriteToRenderer(r)
|
||||||
|
|
||||||
r.MoveTo(canvasBox.Left, canvasBox.Bottom)
|
r.MoveTo(canvasBox.Left, canvasBox.Bottom)
|
||||||
r.LineTo(canvasBox.Right, canvasBox.Bottom)
|
r.LineTo(canvasBox.Right, canvasBox.Bottom)
|
||||||
|
|
4
yaxis.go
4
yaxis.go
|
@ -61,7 +61,7 @@ func (ya YAxis) GetGridLines(ticks []Tick) []GridLine {
|
||||||
|
|
||||||
// Measure returns the bounds of the axis.
|
// Measure returns the bounds of the axis.
|
||||||
func (ya YAxis) Measure(r Renderer, canvasBox Box, ra Range, defaults Style, ticks []Tick) Box {
|
func (ya YAxis) Measure(r Renderer, canvasBox Box, ra Range, defaults Style, ticks []Tick) Box {
|
||||||
ya.Style.InheritFrom(defaults).PersistToRenderer(r)
|
ya.Style.InheritFrom(defaults).WriteToRenderer(r)
|
||||||
|
|
||||||
sort.Sort(Ticks(ticks))
|
sort.Sort(Ticks(ticks))
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ func (ya YAxis) Measure(r Renderer, canvasBox Box, ra Range, defaults Style, tic
|
||||||
|
|
||||||
// Render renders the axis.
|
// Render renders the axis.
|
||||||
func (ya YAxis) Render(r Renderer, canvasBox Box, ra Range, defaults Style, ticks []Tick) {
|
func (ya YAxis) Render(r Renderer, canvasBox Box, ra Range, defaults Style, ticks []Tick) {
|
||||||
ya.Style.InheritFrom(defaults).PersistToRenderer(r)
|
ya.Style.InheritFrom(defaults).WriteToRenderer(r)
|
||||||
|
|
||||||
sort.Sort(Ticks(ticks))
|
sort.Sort(Ticks(ticks))
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue