Skip to content

Commit

Permalink
???
Browse files Browse the repository at this point in the history
  • Loading branch information
Will Charczuk committed Jul 12, 2016
1 parent 01983f4 commit 9a5138b
Show file tree
Hide file tree
Showing 17 changed files with 468 additions and 299 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ go-chart
Package `chart` is a very simple golang native charting library that supports timeseries and continuous
line charts.

The API is still in a bit of flux, so it is adviseable to wait until I tag a v1.0 release before using
in a production capacity.

# Installation

To install `chart` run the following:
Expand Down
28 changes: 10 additions & 18 deletions annotation_series.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,21 +49,13 @@ func (as AnnotationSeries) Measure(r Renderer, canvasBox Box, xrange, yrange Ran
Padding: DefaultAnnotationPadding,
})
for _, a := range as.Annotations {
lx := canvasBox.Right - xrange.Translate(a.X)
ly := yrange.Translate(a.Y) + canvasBox.Top
ab := MeasureAnnotation(r, canvasBox, xrange, yrange, style, lx, ly, a.Label)
if ab.Top < box.Top {
box.Top = ab.Top
}
if ab.Left < box.Left {
box.Left = ab.Left
}
if ab.Right > box.Right {
box.Right = ab.Right
}
if ab.Bottom > box.Bottom {
box.Bottom = ab.Bottom
}
lx := canvasBox.Left - xrange.Translate(a.X)
ly := canvasBox.Bottom - yrange.Translate(a.Y)
ab := MeasureAnnotation(r, canvasBox, style, lx, ly, a.Label)
box.Top = MinInt(box.Top, ab.Top)
box.Left = MinInt(box.Left, ab.Left)
box.Right = MaxInt(box.Right, ab.Right)
box.Bottom = MaxInt(box.Bottom, ab.Bottom)
}
}
return box
Expand All @@ -81,9 +73,9 @@ func (as AnnotationSeries) Render(r Renderer, canvasBox Box, xrange, yrange Rang
Padding: DefaultAnnotationPadding,
})
for _, a := range as.Annotations {
lx := canvasBox.Right - xrange.Translate(a.X)
ly := yrange.Translate(a.Y) + canvasBox.Top
DrawAnnotation(r, canvasBox, xrange, yrange, style, lx, ly, a.Label)
lx := canvasBox.Left + xrange.Translate(a.X)
ly := canvasBox.Bottom - yrange.Translate(a.Y)
DrawAnnotation(r, canvasBox, style, lx, ly, a.Label)
}
}
}
155 changes: 63 additions & 92 deletions chart.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"math"

"github.com/golang/freetype/truetype"
"github.com/wcharczuk/go-chart/drawing"
)

// Chart is what we're drawing.
Expand Down Expand Up @@ -57,6 +56,8 @@ func (c Chart) Render(rp RendererProvider, w io.Writer) error {
if len(c.Series) == 0 {
return errors.New("Please provide at least one series")
}
c.YAxisSecondary.AxisType = YAxisSecondary

r, err := rp(c.Width, c.Height)
if err != nil {
return err
Expand All @@ -80,7 +81,7 @@ func (c Chart) Render(rp RendererProvider, w io.Writer) error {

if c.hasAxes() {
xt, yt, yta = c.getAxesTicks(r, xr, yr, yra, xf, yf, yfa)
canvasBox = c.getAxisAdjustedCanvasBox(r, canvasBox, xt, yt, yta)
canvasBox = c.getAxisAdjustedCanvasBox(r, canvasBox, xr, yr, yra, xt, yt, yta)
xr, yr, yra = c.setRangeDomains(canvasBox, xr, yr, yra)
xt, yt, yta = c.getAxesTicks(r, xr, yr, yra, xf, yf, yfa)
}
Expand Down Expand Up @@ -143,6 +144,7 @@ func (c Chart) getRanges() (xrange, yrange, yrangeAlt Range) {
} else {
xrange.Min = globalMinX
xrange.Max = globalMaxX
xrange.Min, xrange.Max = xrange.GetRoundedRangeBounds()
}

if !c.YAxis.Range.IsZero() {
Expand All @@ -151,6 +153,7 @@ func (c Chart) getRanges() (xrange, yrange, yrangeAlt Range) {
} else {
yrange.Min = globalMinY
yrange.Max = globalMaxY
yrange.Min, yrange.Max = yrange.GetRoundedRangeBounds()
}

if !c.YAxisSecondary.Range.IsZero() {
Expand All @@ -159,6 +162,7 @@ func (c Chart) getRanges() (xrange, yrange, yrangeAlt Range) {
} else {
yrangeAlt.Min = globalMinYA
yrangeAlt.Max = globalMaxYA
yrangeAlt.Min, yrangeAlt.Max = yrangeAlt.GetRoundedRangeBounds()
}

return
Expand Down Expand Up @@ -222,84 +226,63 @@ func (c Chart) getAxesTicks(r Renderer, xr, yr, yar Range, xf, yf, yfa ValueForm
return
}

func (c Chart) getAxisAdjustedCanvasBox(r Renderer, defaults Box, xticks, yticks, yticksAlt []Tick) Box {
canvasBox := Box{}

var dpl, dpr, dpb int
func (c Chart) getAxisAdjustedCanvasBox(r Renderer, canvasBox Box, xr, yr, yra Range, xticks, yticks, yticksAlt []Tick) Box {
axesMinX, axesMaxX, axesMinY, axesMaxY := math.MaxInt32, 0, math.MaxInt32, 0
if c.XAxis.Style.Show {
dpb = c.getXAxisHeight(r, xticks)
axesBounds := c.XAxis.Measure(r, canvasBox, xr, xticks)
axesMinY = MinInt(axesMinX, axesBounds.Top)
axesMinX = MinInt(axesMinY, axesBounds.Left)
axesMaxX = MaxInt(axesMaxX, axesBounds.Right)
axesMaxY = MaxInt(axesMaxY, axesBounds.Bottom)
}
if c.YAxis.Style.Show {
dpr = c.getYAxisWidth(r, yticks)
axesBounds := c.YAxis.Measure(r, canvasBox, yr, yticks)
axesMinY = MinInt(axesMinX, axesBounds.Top)
axesMinX = MinInt(axesMinY, axesBounds.Left)
axesMaxX = MaxInt(axesMaxX, axesBounds.Right)
axesMaxY = MaxInt(axesMaxY, axesBounds.Bottom)
}
if c.YAxisSecondary.Style.Show {
dpl = c.getYAxisSecondaryWidth(r, yticksAlt)
}

canvasBox.Top = defaults.Top
if dpl != 0 {
canvasBox.Left = c.Canvas.Padding.GetLeft(dpl)
} else {
canvasBox.Left = defaults.Left
}
if dpr != 0 {
canvasBox.Right = c.Width - c.Canvas.Padding.GetRight(dpr)
} else {
canvasBox.Right = defaults.Right
axesBounds := c.YAxisSecondary.Measure(r, canvasBox, yra, yticksAlt)
axesMinY = MinInt(axesMinX, axesBounds.Top)
axesMinX = MinInt(axesMinY, axesBounds.Left)
axesMaxX = MaxInt(axesMaxX, axesBounds.Right)
axesMaxY = MaxInt(axesMaxY, axesBounds.Bottom)
}
if dpb != 0 {
canvasBox.Bottom = c.Height - c.Canvas.Padding.GetBottom(dpb)
} else {
canvasBox.Bottom = defaults.Bottom
newBox := Box{
Top: canvasBox.Top,
Left: canvasBox.Left,
Right: canvasBox.Right,
Bottom: canvasBox.Bottom,
}

canvasBox.Width = canvasBox.Right - canvasBox.Left
canvasBox.Height = canvasBox.Bottom - canvasBox.Top
return canvasBox
}

func (c Chart) getXAxisHeight(r Renderer, ticks []Tick) int {
r.SetFontSize(c.XAxis.Style.GetFontSize(DefaultFontSize))
r.SetFont(c.XAxis.Style.GetFont(c.Font))

var textHeight int
for _, t := range ticks {
_, th := r.MeasureText(t.Label)
if th > textHeight {
textHeight = th
}
if axesMinY < 0 {
// figure out how much top padding to add
delta := -1 * axesMinY
newBox.Top = canvasBox.Top + delta
}
return textHeight + (2 * DefaultXAxisMargin) // top and bottom.
}

func (c Chart) getYAxisWidth(r Renderer, ticks []Tick) int {
r.SetFontSize(c.YAxis.Style.GetFontSize(DefaultFontSize))
r.SetFont(c.YAxis.Style.GetFont(c.Font))

var textWidth int
for _, t := range ticks {
tw, _ := r.MeasureText(t.Label)
if tw > textWidth {
textWidth = tw
}
if axesMinX < 0 {
// figure out how much left padding to add
delta := -1 * axesMinX
newBox.Left = canvasBox.Left + delta
}

return textWidth + DefaultYAxisMargin
}

func (c Chart) getYAxisSecondaryWidth(r Renderer, ticks []Tick) int {
r.SetFontSize(c.YAxisSecondary.Style.GetFontSize(DefaultFontSize))
r.SetFont(c.YAxisSecondary.Style.GetFont(c.Font))
if axesMaxX > c.Width {
// figure out how much right padding to add
delta := axesMaxX - c.Width
newBox.Right = canvasBox.Right - delta
}

var textWidth int
for _, t := range ticks {
tw, _ := r.MeasureText(t.Label)
if tw > textWidth {
textWidth = tw
}
if axesMaxY > c.Height {
//figure out how much bottom padding to add
delta := axesMaxY - c.Height
newBox.Bottom = canvasBox.Bottom - delta
}

return textWidth + DefaultYAxisMargin
newBox.Height = newBox.Bottom - newBox.Top
newBox.Width = newBox.Right - newBox.Left
return newBox
}

func (c Chart) setRangeDomains(canvasBox Box, xrange, yrange, yrangeAlt Range) (Range, Range, Range) {
Expand Down Expand Up @@ -333,21 +316,10 @@ func (c Chart) getAnnotationAdjustedCanvasBox(r Renderer, canvasBox Box, xr, yr,
annotationBounds = as.Measure(r, canvasBox, xr, yra, style)
}

if annotationMinY > annotationBounds.Top {
annotationMinY = annotationBounds.Top
}

if annotationMinX > annotationBounds.Left {
annotationMinX = annotationBounds.Left
}

if annotationMaxX < annotationBounds.Right {
annotationMaxX = annotationBounds.Right
}

if annotationMaxY < annotationBounds.Bottom {
annotationMaxY = annotationBounds.Bottom
}
annotationMinY = MinInt(annotationMinY, annotationBounds.Top)
annotationMinX = MinInt(annotationMinX, annotationBounds.Left)
annotationMaxX = MaxInt(annotationMaxX, annotationBounds.Right)
annotationMaxY = MaxInt(annotationMaxY, annotationBounds.Bottom)
}
}
}
Expand All @@ -364,22 +336,21 @@ func (c Chart) getAnnotationAdjustedCanvasBox(r Renderer, canvasBox Box, xr, yr,
newBox.Top = canvasBox.Top + delta
}

if annotationMaxX > c.Width {
// figure out how much right padding to add
delta := annotationMaxX - c.Width
newBox.Right = canvasBox.Right - delta
}

if annotationMinX < 0 {
// figure out how much left padding to add
delta := -1 * annotationMinX
newBox.Left = canvasBox.Left + delta
}

if annotationMaxX > c.Width {
// figure out how much right padding to add
delta := annotationMaxX - c.Width
newBox.Right = canvasBox.Right - delta
}

if annotationMaxY > c.Height {
//figure out how much bottom padding to add
delta := annotationMaxY - c.Height
println("bottom delta", annotationMaxY, c.Height)
newBox.Bottom = canvasBox.Bottom - delta
}

Expand Down Expand Up @@ -420,10 +391,10 @@ func (c Chart) drawAxes(r Renderer, canvasBox Box, xrange, yrange, yrangeAlt Ran
c.XAxis.Render(r, canvasBox, xrange, xticks)
}
if c.YAxis.Style.Show {
c.YAxis.Render(r, canvasBox, yrange, YAxisPrimary, yticks)
c.YAxis.Render(r, canvasBox, yrange, yticks)
}
if c.YAxisSecondary.Style.Show {
c.YAxisSecondary.Render(r, canvasBox, yrangeAlt, YAxisSecondary, yticksAlt)
c.YAxisSecondary.Render(r, canvasBox, yrangeAlt, yticksAlt)
}
}

Expand Down Expand Up @@ -453,10 +424,10 @@ func (c Chart) drawTitle(r Renderer) {
titleFontSize := c.TitleStyle.GetFontSize(DefaultTitleFontSize)
r.SetFontSize(titleFontSize)

textWidthPoints, textHeightPoints := r.MeasureText(c.Title)
textBox := r.MeasureText(c.Title)

textWidth := int(drawing.PointsToPixels(r.GetDPI(), float64(textWidthPoints)))
textHeight := int(drawing.PointsToPixels(r.GetDPI(), float64(textHeightPoints)))
textWidth := textBox.Width
textHeight := textBox.Height

titleX := (c.Width >> 1) - (textWidth >> 1)
titleY := c.TitleStyle.Padding.GetTop(DefaultTitleTop) + textHeight
Expand Down
23 changes: 18 additions & 5 deletions defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,13 @@ const (
// DefaultXAxisMargin is the default distance from bottom of the canvas to the x axis labels.
DefaultXAxisMargin = 10

//DefaultVerticalTickWidth is half the margin.
DefaultVerticalTickWidth = DefaultYAxisMargin >> 1

//DefaultVerticalTickHeight is half the margin.
DefaultVerticalTickHeight = DefaultXAxisMargin >> 1
//DefaultHorizontalTickWidth is half the margin.
DefaultHorizontalTickWidth = DefaultXAxisMargin >> 1
DefaultHorizontalTickWidth = DefaultYAxisMargin >> 1

// DefaultTickCount is the defautl number of ticks to show
DefaultTickCount = 10

// DefaultMinimumTickHorizontalSpacing is the minimum distance between horizontal ticks.
DefaultMinimumTickHorizontalSpacing = 20
Expand Down Expand Up @@ -91,6 +93,17 @@ var (
}
)

var (
// DashArrayDots is a dash array that represents '....' style stroke dashes.
DashArrayDots = []int{1, 1}
// DashArrayDashesSmall is a dash array that represents '- - -' style stroke dashes.
DashArrayDashesSmall = []int{3, 3}
// DashArrayDashesMedium is a dash array that represents '-- -- --' style stroke dashes.
DashArrayDashesMedium = []int{5, 5}
// DashArrayDashesLarge is a dash array that represents '----- ----- -----' style stroke dashes.
DashArrayDashesLarge = []int{10, 10}
)

// GetDefaultSeriesStrokeColor returns a color from the default list by index.
// NOTE: the index will wrap around (using a modulo).g
func GetDefaultSeriesStrokeColor(index int) drawing.Color {
Expand All @@ -100,7 +113,7 @@ func GetDefaultSeriesStrokeColor(index int) drawing.Color {

var (
// DefaultAnnotationPadding is the padding around an annotation.
DefaultAnnotationPadding = Box{Top: 5, Left: 5, Right: 5, Bottom: 5}
DefaultAnnotationPadding = Box{Top: 3, Left: 5, Right: 5, Bottom: 5}
// DefaultBackgroundPadding is the default canvas padding config.
DefaultBackgroundPadding = Box{Top: 5, Left: 5, Right: 5, Bottom: 5}
)
Expand Down
Loading

0 comments on commit 9a5138b

Please sign in to comment.