Compare commits

...

1 commit

Author SHA1 Message Date
Will Charczuk
8721033046 checkin 2017-06-11 14:56:30 -07:00
2 changed files with 140 additions and 40 deletions

View file

@ -10,8 +10,9 @@ import (
func drawChart(res http.ResponseWriter, req *http.Request) { func drawChart(res http.ResponseWriter, req *http.Request) {
sbc := chart.StackedBarChart{ sbc := chart.StackedBarChart{
Title: "Test Stacked Bar Chart", Title: "Test Stacked Bar Chart",
TitleStyle: chart.StyleShow(), TitleStyle: chart.StyleShow(),
Orientation: chart.OrientationLandscape,
Background: chart.Style{ Background: chart.Style{
Padding: chart.Box{ Padding: chart.Box{
Top: 40, Top: 40,

View file

@ -11,6 +11,18 @@ import (
util "github.com/wcharczuk/go-chart/util" util "github.com/wcharczuk/go-chart/util"
) )
// Orientation is an orientation for an element.
type Orientation int
const (
// OrientationDefault is the default orientation.
OrientationDefault Orientation = iota
// OrientationLandscape is the landscape orientation.
OrientationLandscape Orientation = iota
// OrientationPortrait is the portrait orientation.
OrientationPortrait Orientation = iota
)
// StackedBar is a bar within a StackedBarChart. // StackedBar is a bar within a StackedBarChart.
type StackedBar struct { type StackedBar struct {
Name string Name string
@ -43,7 +55,8 @@ type StackedBarChart struct {
XAxis Style XAxis Style
YAxis Style YAxis Style
BarSpacing int BarSpacing int
Orientation Orientation
Font *truetype.Font Font *truetype.Font
defaultFont *truetype.Font defaultFont *truetype.Font
@ -95,6 +108,16 @@ func (sbc StackedBarChart) GetBarSpacing() int {
return sbc.BarSpacing return sbc.BarSpacing
} }
// GetOrientation gets the orientation or the default.
func (sbc StackedBarChart) GetOrientation() Orientation {
switch sbc.Orientation {
case OrientationDefault:
return OrientationPortrait
default:
return sbc.Orientation
}
}
// Render renders the chart with the given renderer to the given io.Writer. // Render renders the chart with the given renderer to the given io.Writer.
func (sbc StackedBarChart) Render(rp RendererProvider, w io.Writer) error { func (sbc StackedBarChart) Render(rp RendererProvider, w io.Writer) error {
if len(sbc.Bars) == 0 { if len(sbc.Bars) == 0 {
@ -134,33 +157,63 @@ func (sbc StackedBarChart) drawCanvas(r Renderer, canvasBox Box) {
} }
func (sbc StackedBarChart) drawBars(r Renderer, canvasBox Box) { func (sbc StackedBarChart) drawBars(r Renderer, canvasBox Box) {
xoffset := canvasBox.Left var offset int
if sbc.GetOrientation() == OrientationPortrait {
offset = canvasBox.Left
} else {
offset = canvasBox.Top
}
for _, bar := range sbc.Bars { for _, bar := range sbc.Bars {
sbc.drawBar(r, canvasBox, xoffset, bar) sbc.drawBar(r, canvasBox, offset, bar)
xoffset += (sbc.GetBarSpacing() + bar.GetWidth()) offset += (sbc.GetBarSpacing() + bar.GetWidth())
} }
} }
func (sbc StackedBarChart) drawBar(r Renderer, canvasBox Box, xoffset int, bar StackedBar) int { func (sbc StackedBarChart) drawBar(r Renderer, canvasBox Box, offset int, bar StackedBar) int {
barSpacing2 := sbc.GetBarSpacing() >> 1 barSpacing2 := sbc.GetBarSpacing() >> 1
bxl := xoffset + barSpacing2
bxr := bxl + bar.GetWidth()
normalizedBarComponents := Values(bar.Values).Normalize() normalizedBarComponents := Values(bar.Values).Normalize()
yoffset := canvasBox.Top
if sbc.GetOrientation() == OrientationPortrait {
bxl := offset + barSpacing2
bxr := bxl + bar.GetWidth()
normalizedBarComponents := Values(bar.Values).Normalize()
yoffset := canvasBox.Top
for index, bv := range normalizedBarComponents {
barHeight := int(math.Ceil(bv.Value * float64(canvasBox.Height())))
barBox := Box{
Top: yoffset,
Left: bxl,
Right: bxr,
Bottom: util.Math.MinInt(yoffset+barHeight, canvasBox.Bottom-DefaultStrokeWidth),
}
Draw.Box(r, barBox, bv.Style.InheritFrom(sbc.styleDefaultsStackedBarValue(index)))
yoffset += barHeight
}
return bxr
}
bxt := offset + barSpacing2
bxb := bxt + bar.GetWidth()
xoffset := canvasBox.Left
for index, bv := range normalizedBarComponents { for index, bv := range normalizedBarComponents {
barHeight := int(math.Ceil(bv.Value * float64(canvasBox.Height()))) barHeight := int(math.Ceil(bv.Value * float64(canvasBox.Height())))
barBox := Box{ barBox := Box{
Top: yoffset, Top: bxt,
Left: bxl, Left: xoffset,
Right: bxr, Right: util.Math.MinInt(xoffset+barHeight, canvasBox.Right-DefaultStrokeWidth),
Bottom: util.Math.MinInt(yoffset+barHeight, canvasBox.Bottom-DefaultStrokeWidth), Bottom: bxb,
} }
Draw.Box(r, barBox, bv.Style.InheritFrom(sbc.styleDefaultsStackedBarValue(index))) Draw.Box(r, barBox, bv.Style.InheritFrom(sbc.styleDefaultsStackedBarValue(index)))
yoffset += barHeight xoffset += barHeight
} }
return bxr return bxb
} }
func (sbc StackedBarChart) drawXAxis(r Renderer, canvasBox Box) { func (sbc StackedBarChart) drawXAxis(r Renderer, canvasBox Box) {
@ -178,7 +231,6 @@ func (sbc StackedBarChart) drawXAxis(r Renderer, canvasBox Box) {
cursor := canvasBox.Left cursor := canvasBox.Left
for _, bar := range sbc.Bars { for _, bar := range sbc.Bars {
barLabelBox := Box{ barLabelBox := Box{
Top: canvasBox.Bottom + DefaultXAxisMargin, Top: canvasBox.Bottom + DefaultXAxisMargin,
Left: cursor, Left: cursor,
@ -271,46 +323,93 @@ func (sbc StackedBarChart) getDefaultCanvasBox() Box {
} }
func (sbc StackedBarChart) getAdjustedCanvasBox(r Renderer, canvasBox Box) Box { func (sbc StackedBarChart) getAdjustedCanvasBox(r Renderer, canvasBox Box) Box {
var totalWidth int var total int
for _, bar := range sbc.Bars { for _, bar := range sbc.Bars {
totalWidth += bar.GetWidth() + sbc.GetBarSpacing() total += bar.GetWidth() + sbc.GetBarSpacing()
} }
if sbc.XAxis.Show { if sbc.XAxis.Show {
xaxisHeight := DefaultVerticalTickHeight if sbc.GetOrientation() == OrientationPortrait {
return Box{
axisStyle := sbc.XAxis.InheritFrom(sbc.styleDefaultsAxes()) Top: canvasBox.Top,
axisStyle.WriteToRenderer(r) Left: canvasBox.Left,
Right: canvasBox.Left + total,
cursor := canvasBox.Left Bottom: sbc.GetHeight() - sbc.measurePortraitAxisHeight(r, canvasBox),
for _, bar := range sbc.Bars {
if len(bar.Name) > 0 {
barLabelBox := Box{
Top: canvasBox.Bottom + DefaultXAxisMargin,
Left: cursor,
Right: cursor + bar.GetWidth() + sbc.GetBarSpacing(),
Bottom: sbc.GetHeight(),
}
lines := Text.WrapFit(r, bar.Name, barLabelBox.Width(), axisStyle)
linesBox := Text.MeasureLines(r, lines, axisStyle)
xaxisHeight = util.Math.MaxInt(linesBox.Height()+(2*DefaultXAxisMargin), xaxisHeight)
} }
} }
return Box{ return Box{
Top: canvasBox.Top, Top: canvasBox.Top,
Left: canvasBox.Left, Left: canvasBox.Left,
Right: canvasBox.Left + totalWidth, Right: sbc.GetWidth() - sbc.measureLandscapeAxisWidth(r, canvasBox),
Bottom: sbc.GetHeight() - xaxisHeight, Bottom: canvasBox.Top + total,
}
}
if sbc.GetOrientation() == OrientationPortrait {
return Box{
Top: canvasBox.Top,
Left: canvasBox.Left,
Right: canvasBox.Left,
Bottom: canvasBox.Top + total,
} }
} }
return Box{ return Box{
Top: canvasBox.Top, Top: canvasBox.Top,
Left: canvasBox.Left, Left: canvasBox.Left,
Right: canvasBox.Left + totalWidth, Right: canvasBox.Left + total,
Bottom: canvasBox.Bottom, Bottom: canvasBox.Bottom,
} }
}
func (sbc StackedBarChart) measurePortraitAxisHeight(r Renderer, canvasBox Box) int {
xaxisHeight := DefaultVerticalTickHeight
axisStyle := sbc.XAxis.InheritFrom(sbc.styleDefaultsAxes())
axisStyle.WriteToRenderer(r)
cursor := canvasBox.Left
for _, bar := range sbc.Bars {
if len(bar.Name) > 0 {
barLabelBox := Box{
Top: canvasBox.Bottom + DefaultXAxisMargin,
Left: cursor,
Right: cursor + bar.GetWidth() + sbc.GetBarSpacing(),
Bottom: sbc.GetHeight(),
}
lines := Text.WrapFit(r, bar.Name, barLabelBox.Width(), axisStyle)
linesBox := Text.MeasureLines(r, lines, axisStyle)
xaxisHeight = util.Math.MaxInt(linesBox.Height()+(2*DefaultXAxisMargin), xaxisHeight)
}
}
return xaxisHeight
}
func (sbc StackedBarChart) measureLandscapeAxisWidth(r Renderer, canvasBox Box) int {
axisWidth := DefaultVerticalTickHeight
axisStyle := sbc.XAxis.InheritFrom(sbc.styleDefaultsAxes())
axisStyle.WriteToRenderer(r)
cursor := canvasBox.Top
for _, bar := range sbc.Bars {
if len(bar.Name) > 0 {
barLabelBox := Box{
Top: cursor,
Left: canvasBox.Right + DefaultXAxisMargin,
Right: sbc.GetWidth(),
Bottom: cursor + bar.GetWidth() + sbc.GetBarSpacing(),
}
lines := Text.WrapFit(r, bar.Name, barLabelBox.Width(), axisStyle)
linesBox := Text.MeasureLines(r, lines, axisStyle)
axisWidth = util.Math.MaxInt(linesBox.Width()+(2*DefaultYAxisMargin), axisWidth)
cursor += bar.GetWidth() + sbc.GetBarSpacing()
}
}
return axisWidth
} }
// Box returns the chart bounds as a box. // Box returns the chart bounds as a box.