This commit is contained in:
Will Charczuk 2017-06-11 14:56:30 -07:00
parent a0ea012903
commit 8721033046
2 changed files with 140 additions and 40 deletions

View file

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

View file

@ -11,6 +11,18 @@ import (
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.
type StackedBar struct {
Name string
@ -44,6 +56,7 @@ type StackedBarChart struct {
YAxis Style
BarSpacing int
Orientation Orientation
Font *truetype.Font
defaultFont *truetype.Font
@ -95,6 +108,16 @@ func (sbc StackedBarChart) GetBarSpacing() int {
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.
func (sbc StackedBarChart) Render(rp RendererProvider, w io.Writer) error {
if len(sbc.Bars) == 0 {
@ -134,16 +157,26 @@ func (sbc StackedBarChart) drawCanvas(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 {
sbc.drawBar(r, canvasBox, xoffset, bar)
xoffset += (sbc.GetBarSpacing() + bar.GetWidth())
sbc.drawBar(r, canvasBox, offset, bar)
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
bxl := xoffset + barSpacing2
normalizedBarComponents := Values(bar.Values).Normalize()
if sbc.GetOrientation() == OrientationPortrait {
bxl := offset + barSpacing2
bxr := bxl + bar.GetWidth()
normalizedBarComponents := Values(bar.Values).Normalize()
@ -161,6 +194,26 @@ func (sbc StackedBarChart) drawBar(r Renderer, canvasBox Box, xoffset int, bar S
}
return bxr
}
bxt := offset + barSpacing2
bxb := bxt + bar.GetWidth()
xoffset := canvasBox.Left
for index, bv := range normalizedBarComponents {
barHeight := int(math.Ceil(bv.Value * float64(canvasBox.Height())))
barBox := Box{
Top: bxt,
Left: xoffset,
Right: util.Math.MinInt(xoffset+barHeight, canvasBox.Right-DefaultStrokeWidth),
Bottom: bxb,
}
Draw.Box(r, barBox, bv.Style.InheritFrom(sbc.styleDefaultsStackedBarValue(index)))
xoffset += barHeight
}
return bxb
}
func (sbc StackedBarChart) drawXAxis(r Renderer, canvasBox Box) {
@ -178,7 +231,6 @@ func (sbc StackedBarChart) drawXAxis(r Renderer, canvasBox Box) {
cursor := canvasBox.Left
for _, bar := range sbc.Bars {
barLabelBox := Box{
Top: canvasBox.Bottom + DefaultXAxisMargin,
Left: cursor,
@ -271,12 +323,47 @@ func (sbc StackedBarChart) getDefaultCanvasBox() Box {
}
func (sbc StackedBarChart) getAdjustedCanvasBox(r Renderer, canvasBox Box) Box {
var totalWidth int
var total int
for _, bar := range sbc.Bars {
totalWidth += bar.GetWidth() + sbc.GetBarSpacing()
total += bar.GetWidth() + sbc.GetBarSpacing()
}
if sbc.XAxis.Show {
if sbc.GetOrientation() == OrientationPortrait {
return Box{
Top: canvasBox.Top,
Left: canvasBox.Left,
Right: canvasBox.Left + total,
Bottom: sbc.GetHeight() - sbc.measurePortraitAxisHeight(r, canvasBox),
}
}
return Box{
Top: canvasBox.Top,
Left: canvasBox.Left,
Right: sbc.GetWidth() - sbc.measureLandscapeAxisWidth(r, canvasBox),
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{
Top: canvasBox.Top,
Left: canvasBox.Left,
Right: canvasBox.Left + total,
Bottom: canvasBox.Bottom,
}
}
func (sbc StackedBarChart) measurePortraitAxisHeight(r Renderer, canvasBox Box) int {
xaxisHeight := DefaultVerticalTickHeight
axisStyle := sbc.XAxis.InheritFrom(sbc.styleDefaultsAxes())
@ -297,20 +384,32 @@ func (sbc StackedBarChart) getAdjustedCanvasBox(r Renderer, canvasBox Box) Box {
xaxisHeight = util.Math.MaxInt(linesBox.Height()+(2*DefaultXAxisMargin), xaxisHeight)
}
}
return Box{
Top: canvasBox.Top,
Left: canvasBox.Left,
Right: canvasBox.Left + totalWidth,
Bottom: sbc.GetHeight() - xaxisHeight,
}
}
return Box{
Top: canvasBox.Top,
Left: canvasBox.Left,
Right: canvasBox.Left + totalWidth,
Bottom: canvasBox.Bottom,
}
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.