2016-07-28 21:51:55 -04:00
|
|
|
package chart
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
2016-07-29 22:35:18 -04:00
|
|
|
"fmt"
|
2016-07-28 21:51:55 -04:00
|
|
|
"io"
|
2016-07-29 22:40:41 -04:00
|
|
|
"math"
|
2016-07-28 21:51:55 -04:00
|
|
|
|
|
|
|
"github.com/golang/freetype/truetype"
|
|
|
|
)
|
|
|
|
|
|
|
|
// StackedBar is a bar within a StackedBarChart.
|
|
|
|
type StackedBar struct {
|
|
|
|
Name string
|
|
|
|
Width int
|
|
|
|
Values []Value
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetWidth returns the width of the bar.
|
|
|
|
func (sb StackedBar) GetWidth() int {
|
|
|
|
if sb.Width == 0 {
|
2016-07-29 22:35:18 -04:00
|
|
|
return 50
|
2016-07-28 21:51:55 -04:00
|
|
|
}
|
|
|
|
return sb.Width
|
|
|
|
}
|
|
|
|
|
|
|
|
// StackedBarChart is a chart that draws sections of a bar based on percentages.
|
|
|
|
type StackedBarChart struct {
|
|
|
|
Title string
|
|
|
|
TitleStyle Style
|
|
|
|
|
2017-06-11 14:38:10 -04:00
|
|
|
ColorPalette ColorPalette
|
|
|
|
|
2016-07-28 21:51:55 -04:00
|
|
|
Width int
|
|
|
|
Height int
|
|
|
|
DPI float64
|
|
|
|
|
|
|
|
Background Style
|
|
|
|
Canvas Style
|
|
|
|
|
2016-07-29 22:35:18 -04:00
|
|
|
XAxis Style
|
|
|
|
YAxis Style
|
|
|
|
|
2016-07-28 21:51:55 -04:00
|
|
|
BarSpacing int
|
|
|
|
|
|
|
|
Font *truetype.Font
|
|
|
|
defaultFont *truetype.Font
|
|
|
|
|
2019-12-06 14:22:51 -05:00
|
|
|
IsHorizontal bool
|
|
|
|
|
2016-07-28 21:51:55 -04:00
|
|
|
Bars []StackedBar
|
|
|
|
Elements []Renderable
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetDPI returns the dpi for the chart.
|
|
|
|
func (sbc StackedBarChart) GetDPI(defaults ...float64) float64 {
|
|
|
|
if sbc.DPI == 0 {
|
|
|
|
if len(defaults) > 0 {
|
|
|
|
return defaults[0]
|
|
|
|
}
|
|
|
|
return DefaultDPI
|
|
|
|
}
|
|
|
|
return sbc.DPI
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetFont returns the text font.
|
|
|
|
func (sbc StackedBarChart) GetFont() *truetype.Font {
|
|
|
|
if sbc.Font == nil {
|
|
|
|
return sbc.defaultFont
|
|
|
|
}
|
|
|
|
return sbc.Font
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetWidth returns the chart width or the default value.
|
|
|
|
func (sbc StackedBarChart) GetWidth() int {
|
|
|
|
if sbc.Width == 0 {
|
|
|
|
return DefaultChartWidth
|
|
|
|
}
|
|
|
|
return sbc.Width
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetHeight returns the chart height or the default value.
|
|
|
|
func (sbc StackedBarChart) GetHeight() int {
|
|
|
|
if sbc.Height == 0 {
|
|
|
|
return DefaultChartWidth
|
|
|
|
}
|
|
|
|
return sbc.Height
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetBarSpacing returns the spacing between bars.
|
|
|
|
func (sbc StackedBarChart) GetBarSpacing() int {
|
|
|
|
if sbc.BarSpacing == 0 {
|
|
|
|
return 100
|
|
|
|
}
|
|
|
|
return sbc.BarSpacing
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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 {
|
2017-02-03 14:26:53 -05:00
|
|
|
return errors.New("please provide at least one bar")
|
2016-07-28 21:51:55 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
r, err := rp(sbc.GetWidth(), sbc.GetHeight())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if sbc.Font == nil {
|
|
|
|
defaultFont, err := GetDefaultFont()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
sbc.defaultFont = defaultFont
|
|
|
|
}
|
|
|
|
r.SetDPI(sbc.GetDPI(DefaultDPI))
|
|
|
|
|
2024-01-10 15:44:02 -05:00
|
|
|
sbc.drawBackground(r)
|
|
|
|
|
2019-12-06 14:22:51 -05:00
|
|
|
var canvasBox Box
|
|
|
|
if sbc.IsHorizontal {
|
|
|
|
canvasBox = sbc.getHorizontalAdjustedCanvasBox(r, sbc.getDefaultCanvasBox())
|
|
|
|
sbc.drawCanvas(r, canvasBox)
|
|
|
|
sbc.drawHorizontalBars(r, canvasBox)
|
|
|
|
sbc.drawHorizontalXAxis(r, canvasBox)
|
|
|
|
sbc.drawHorizontalYAxis(r, canvasBox)
|
|
|
|
} else {
|
|
|
|
canvasBox = sbc.getAdjustedCanvasBox(r, sbc.getDefaultCanvasBox())
|
|
|
|
sbc.drawCanvas(r, canvasBox)
|
|
|
|
sbc.drawBars(r, canvasBox)
|
|
|
|
sbc.drawXAxis(r, canvasBox)
|
|
|
|
sbc.drawYAxis(r, canvasBox)
|
|
|
|
}
|
2016-07-28 21:51:55 -04:00
|
|
|
|
|
|
|
sbc.drawTitle(r)
|
|
|
|
for _, a := range sbc.Elements {
|
|
|
|
a(r, canvasBox, sbc.styleDefaultsElements())
|
|
|
|
}
|
|
|
|
|
|
|
|
return r.Save(w)
|
|
|
|
}
|
|
|
|
|
2017-06-11 14:38:10 -04:00
|
|
|
func (sbc StackedBarChart) drawCanvas(r Renderer, canvasBox Box) {
|
|
|
|
Draw.Box(r, canvasBox, sbc.getCanvasStyle())
|
|
|
|
}
|
|
|
|
|
2016-07-28 21:51:55 -04:00
|
|
|
func (sbc StackedBarChart) drawBars(r Renderer, canvasBox Box) {
|
|
|
|
xoffset := canvasBox.Left
|
|
|
|
for _, bar := range sbc.Bars {
|
|
|
|
sbc.drawBar(r, canvasBox, xoffset, bar)
|
2016-07-30 02:13:33 -04:00
|
|
|
xoffset += (sbc.GetBarSpacing() + bar.GetWidth())
|
2016-07-28 21:51:55 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-10 15:44:02 -05:00
|
|
|
func (sbc StackedBarChart) drawBackground(r Renderer) {
|
|
|
|
Draw.Box(r, Box{
|
|
|
|
Right: sbc.GetWidth(),
|
|
|
|
Bottom: sbc.GetHeight(),
|
|
|
|
}, sbc.getBackgroundStyle())
|
|
|
|
}
|
|
|
|
|
|
|
|
func (sbc StackedBarChart) getBackgroundStyle() Style {
|
|
|
|
return sbc.Background.InheritFrom(sbc.styleDefaultsBackground())
|
|
|
|
}
|
|
|
|
|
|
|
|
func (sbc StackedBarChart) styleDefaultsBackground() Style {
|
|
|
|
return Style{
|
|
|
|
FillColor: sbc.GetColorPalette().BackgroundColor(),
|
|
|
|
StrokeColor: sbc.GetColorPalette().BackgroundStrokeColor(),
|
|
|
|
StrokeWidth: DefaultStrokeWidth,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-06 14:22:51 -05:00
|
|
|
func (sbc StackedBarChart) drawHorizontalBars(r Renderer, canvasBox Box) {
|
|
|
|
yOffset := canvasBox.Top
|
|
|
|
for _, bar := range sbc.Bars {
|
|
|
|
sbc.drawHorizontalBar(r, canvasBox, yOffset, bar)
|
|
|
|
yOffset += sbc.GetBarSpacing() + bar.GetWidth()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-28 21:51:55 -04:00
|
|
|
func (sbc StackedBarChart) drawBar(r Renderer, canvasBox Box, xoffset int, bar StackedBar) int {
|
2016-07-30 02:13:33 -04:00
|
|
|
barSpacing2 := sbc.GetBarSpacing() >> 1
|
|
|
|
bxl := xoffset + barSpacing2
|
2016-07-29 22:35:18 -04:00
|
|
|
bxr := bxl + bar.GetWidth()
|
2016-07-28 21:51:55 -04:00
|
|
|
|
|
|
|
normalizedBarComponents := Values(bar.Values).Normalize()
|
|
|
|
yoffset := canvasBox.Top
|
|
|
|
for index, bv := range normalizedBarComponents {
|
2016-07-29 22:40:41 -04:00
|
|
|
barHeight := int(math.Ceil(bv.Value * float64(canvasBox.Height())))
|
2016-07-30 02:13:33 -04:00
|
|
|
barBox := Box{
|
|
|
|
Top: yoffset,
|
|
|
|
Left: bxl,
|
|
|
|
Right: bxr,
|
2019-02-13 19:09:26 -05:00
|
|
|
Bottom: MinInt(yoffset+barHeight, canvasBox.Bottom-DefaultStrokeWidth),
|
2016-07-30 02:13:33 -04:00
|
|
|
}
|
2016-07-29 19:36:29 -04:00
|
|
|
Draw.Box(r, barBox, bv.Style.InheritFrom(sbc.styleDefaultsStackedBarValue(index)))
|
2016-07-28 21:51:55 -04:00
|
|
|
yoffset += barHeight
|
|
|
|
}
|
|
|
|
|
2019-12-06 14:22:51 -05:00
|
|
|
// draw the labels
|
|
|
|
yoffset = canvasBox.Top
|
|
|
|
var lx, ly int
|
|
|
|
for index, bv := range normalizedBarComponents {
|
|
|
|
barHeight := int(math.Ceil(bv.Value * float64(canvasBox.Height())))
|
|
|
|
|
|
|
|
if len(bv.Label) > 0 {
|
|
|
|
lx = bxl + ((bxr - bxl) / 2)
|
|
|
|
ly = yoffset + (barHeight / 2)
|
|
|
|
|
|
|
|
bv.Style.InheritFrom(sbc.styleDefaultsStackedBarValue(index)).WriteToRenderer(r)
|
|
|
|
tb := r.MeasureText(bv.Label)
|
|
|
|
lx = lx - (tb.Width() >> 1)
|
|
|
|
ly = ly + (tb.Height() >> 1)
|
|
|
|
|
|
|
|
if lx < 0 {
|
|
|
|
lx = 0
|
|
|
|
}
|
|
|
|
if ly < 0 {
|
|
|
|
lx = 0
|
|
|
|
}
|
|
|
|
|
|
|
|
r.Text(bv.Label, lx, ly)
|
|
|
|
}
|
|
|
|
yoffset += barHeight
|
|
|
|
}
|
|
|
|
|
2016-07-28 21:51:55 -04:00
|
|
|
return bxr
|
|
|
|
}
|
|
|
|
|
2019-12-06 14:22:51 -05:00
|
|
|
func (sbc StackedBarChart) drawHorizontalBar(r Renderer, canvasBox Box, yoffset int, bar StackedBar) {
|
|
|
|
halfBarSpacing := sbc.GetBarSpacing() >> 1
|
|
|
|
|
|
|
|
boxTop := yoffset + halfBarSpacing
|
|
|
|
boxBottom := boxTop + bar.GetWidth()
|
|
|
|
|
|
|
|
normalizedBarComponents := Values(bar.Values).Normalize()
|
|
|
|
|
|
|
|
xOffset := canvasBox.Right
|
|
|
|
for index, bv := range normalizedBarComponents {
|
|
|
|
barHeight := int(math.Ceil(bv.Value * float64(canvasBox.Width())))
|
|
|
|
barBox := Box{
|
|
|
|
Top: boxTop,
|
|
|
|
Left: MinInt(xOffset-barHeight, canvasBox.Left+DefaultStrokeWidth),
|
|
|
|
Right: xOffset,
|
|
|
|
Bottom: boxBottom,
|
|
|
|
}
|
|
|
|
Draw.Box(r, barBox, bv.Style.InheritFrom(sbc.styleDefaultsStackedBarValue(index)))
|
|
|
|
xOffset -= barHeight
|
|
|
|
}
|
|
|
|
|
|
|
|
// draw the labels
|
|
|
|
xOffset = canvasBox.Right
|
|
|
|
var lx, ly int
|
|
|
|
for index, bv := range normalizedBarComponents {
|
|
|
|
barHeight := int(math.Ceil(bv.Value * float64(canvasBox.Width())))
|
|
|
|
|
|
|
|
if len(bv.Label) > 0 {
|
|
|
|
lx = xOffset - (barHeight / 2)
|
|
|
|
ly = boxTop + ((boxBottom - boxTop) / 2)
|
|
|
|
|
|
|
|
bv.Style.InheritFrom(sbc.styleDefaultsStackedBarValue(index)).WriteToRenderer(r)
|
|
|
|
tb := r.MeasureText(bv.Label)
|
|
|
|
lx = lx - (tb.Width() >> 1)
|
|
|
|
ly = ly + (tb.Height() >> 1)
|
|
|
|
|
|
|
|
if lx < 0 {
|
|
|
|
lx = 0
|
|
|
|
}
|
|
|
|
if ly < 0 {
|
|
|
|
lx = 0
|
|
|
|
}
|
|
|
|
|
|
|
|
r.Text(bv.Label, lx, ly)
|
|
|
|
}
|
|
|
|
xOffset -= barHeight
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-29 22:35:18 -04:00
|
|
|
func (sbc StackedBarChart) drawXAxis(r Renderer, canvasBox Box) {
|
2019-02-13 21:55:13 -05:00
|
|
|
if !sbc.XAxis.Hidden {
|
2016-07-29 22:35:18 -04:00
|
|
|
axisStyle := sbc.XAxis.InheritFrom(sbc.styleDefaultsAxes())
|
|
|
|
axisStyle.WriteToRenderer(r)
|
|
|
|
|
|
|
|
r.MoveTo(canvasBox.Left, canvasBox.Bottom)
|
|
|
|
r.LineTo(canvasBox.Right, canvasBox.Bottom)
|
|
|
|
r.Stroke()
|
|
|
|
|
|
|
|
r.MoveTo(canvasBox.Left, canvasBox.Bottom)
|
|
|
|
r.LineTo(canvasBox.Left, canvasBox.Bottom+DefaultVerticalTickHeight)
|
|
|
|
r.Stroke()
|
|
|
|
|
|
|
|
cursor := canvasBox.Left
|
|
|
|
for _, bar := range sbc.Bars {
|
|
|
|
|
|
|
|
barLabelBox := Box{
|
|
|
|
Top: canvasBox.Bottom + DefaultXAxisMargin,
|
|
|
|
Left: cursor,
|
2016-07-30 02:13:33 -04:00
|
|
|
Right: cursor + bar.GetWidth() + sbc.GetBarSpacing(),
|
2016-07-29 22:35:18 -04:00
|
|
|
Bottom: sbc.GetHeight(),
|
|
|
|
}
|
|
|
|
if len(bar.Name) > 0 {
|
|
|
|
Draw.TextWithin(r, bar.Name, barLabelBox, axisStyle)
|
|
|
|
}
|
|
|
|
axisStyle.WriteToRenderer(r)
|
|
|
|
r.MoveTo(barLabelBox.Right, canvasBox.Bottom)
|
|
|
|
r.LineTo(barLabelBox.Right, canvasBox.Bottom+DefaultVerticalTickHeight)
|
|
|
|
r.Stroke()
|
2016-07-30 02:13:33 -04:00
|
|
|
cursor += bar.GetWidth() + sbc.GetBarSpacing()
|
2016-07-29 22:35:18 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-07-28 21:51:55 -04:00
|
|
|
|
2019-12-06 14:22:51 -05:00
|
|
|
func (sbc StackedBarChart) drawHorizontalXAxis(r Renderer, canvasBox Box) {
|
|
|
|
if !sbc.XAxis.Hidden {
|
|
|
|
axisStyle := sbc.XAxis.InheritFrom(sbc.styleDefaultsAxes())
|
|
|
|
axisStyle.WriteToRenderer(r)
|
|
|
|
r.MoveTo(canvasBox.Left, canvasBox.Bottom)
|
|
|
|
r.LineTo(canvasBox.Right, canvasBox.Bottom)
|
|
|
|
r.Stroke()
|
|
|
|
|
|
|
|
r.MoveTo(canvasBox.Left, canvasBox.Bottom)
|
|
|
|
r.LineTo(canvasBox.Left, canvasBox.Bottom+DefaultVerticalTickHeight)
|
|
|
|
r.Stroke()
|
|
|
|
|
|
|
|
ticks := LinearRangeWithStep(0.0, 1.0, 0.2)
|
|
|
|
for _, t := range ticks {
|
|
|
|
axisStyle.GetStrokeOptions().WriteToRenderer(r)
|
|
|
|
tx := canvasBox.Left + int(t*float64(canvasBox.Width()))
|
|
|
|
r.MoveTo(tx, canvasBox.Bottom)
|
|
|
|
r.LineTo(tx, canvasBox.Bottom+DefaultVerticalTickHeight)
|
|
|
|
r.Stroke()
|
|
|
|
|
|
|
|
axisStyle.GetTextOptions().WriteToRenderer(r)
|
|
|
|
text := fmt.Sprintf("%0.0f%%", t*100)
|
|
|
|
|
|
|
|
textBox := r.MeasureText(text)
|
|
|
|
textX := tx - (textBox.Width() >> 1)
|
|
|
|
textY := canvasBox.Bottom + DefaultXAxisMargin + 10
|
|
|
|
|
|
|
|
if t == 1 {
|
|
|
|
textX = canvasBox.Right - textBox.Width()
|
|
|
|
}
|
|
|
|
|
|
|
|
Draw.Text(r, text, textX, textY, axisStyle)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-29 22:35:18 -04:00
|
|
|
func (sbc StackedBarChart) drawYAxis(r Renderer, canvasBox Box) {
|
2019-02-13 21:55:13 -05:00
|
|
|
if !sbc.YAxis.Hidden {
|
2016-07-29 22:35:18 -04:00
|
|
|
axisStyle := sbc.YAxis.InheritFrom(sbc.styleDefaultsAxes())
|
|
|
|
axisStyle.WriteToRenderer(r)
|
|
|
|
r.MoveTo(canvasBox.Right, canvasBox.Top)
|
|
|
|
r.LineTo(canvasBox.Right, canvasBox.Bottom)
|
|
|
|
r.Stroke()
|
|
|
|
|
|
|
|
r.MoveTo(canvasBox.Right, canvasBox.Bottom)
|
|
|
|
r.LineTo(canvasBox.Right+DefaultHorizontalTickWidth, canvasBox.Bottom)
|
|
|
|
r.Stroke()
|
|
|
|
|
2019-02-13 21:55:13 -05:00
|
|
|
ticks := LinearRangeWithStep(0.0, 1.0, 0.2)
|
2016-07-29 22:35:18 -04:00
|
|
|
for _, t := range ticks {
|
2016-07-29 22:38:37 -04:00
|
|
|
axisStyle.GetStrokeOptions().WriteToRenderer(r)
|
2016-07-29 22:35:18 -04:00
|
|
|
ty := canvasBox.Bottom - int(t*float64(canvasBox.Height()))
|
|
|
|
r.MoveTo(canvasBox.Right, ty)
|
|
|
|
r.LineTo(canvasBox.Right+DefaultHorizontalTickWidth, ty)
|
|
|
|
r.Stroke()
|
|
|
|
|
2016-07-29 22:38:37 -04:00
|
|
|
axisStyle.GetTextOptions().WriteToRenderer(r)
|
2016-07-29 22:35:18 -04:00
|
|
|
text := fmt.Sprintf("%0.0f%%", t*100)
|
2016-07-29 22:38:37 -04:00
|
|
|
|
|
|
|
tb := r.MeasureText(text)
|
|
|
|
Draw.Text(r, text, canvasBox.Right+DefaultYAxisMargin+5, ty+(tb.Height()>>1), axisStyle)
|
2016-07-29 22:35:18 -04:00
|
|
|
}
|
2019-12-06 14:22:51 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (sbc StackedBarChart) drawHorizontalYAxis(r Renderer, canvasBox Box) {
|
|
|
|
if !sbc.YAxis.Hidden {
|
|
|
|
axisStyle := sbc.YAxis.InheritFrom(sbc.styleDefaultsHorizontalAxes())
|
|
|
|
axisStyle.WriteToRenderer(r)
|
|
|
|
|
|
|
|
r.MoveTo(canvasBox.Left, canvasBox.Bottom)
|
|
|
|
r.LineTo(canvasBox.Left, canvasBox.Top)
|
|
|
|
r.Stroke()
|
2016-07-28 21:51:55 -04:00
|
|
|
|
2019-12-06 14:22:51 -05:00
|
|
|
r.MoveTo(canvasBox.Left, canvasBox.Bottom)
|
|
|
|
r.LineTo(canvasBox.Left-DefaultHorizontalTickWidth, canvasBox.Bottom)
|
|
|
|
r.Stroke()
|
|
|
|
|
|
|
|
cursor := canvasBox.Top
|
|
|
|
for _, bar := range sbc.Bars {
|
|
|
|
barLabelBox := Box{
|
|
|
|
Top: cursor,
|
|
|
|
Left: 0,
|
|
|
|
Right: canvasBox.Left - DefaultYAxisMargin,
|
|
|
|
Bottom: cursor + bar.GetWidth() + sbc.GetBarSpacing(),
|
|
|
|
}
|
|
|
|
if len(bar.Name) > 0 {
|
|
|
|
Draw.TextWithin(r, bar.Name, barLabelBox, axisStyle)
|
|
|
|
}
|
|
|
|
axisStyle.WriteToRenderer(r)
|
|
|
|
r.MoveTo(canvasBox.Left, barLabelBox.Bottom)
|
|
|
|
r.LineTo(canvasBox.Left-DefaultHorizontalTickWidth, barLabelBox.Bottom)
|
|
|
|
r.Stroke()
|
|
|
|
cursor += bar.GetWidth() + sbc.GetBarSpacing()
|
|
|
|
}
|
2016-07-29 22:35:18 -04:00
|
|
|
}
|
|
|
|
}
|
2016-07-28 21:51:55 -04:00
|
|
|
|
2016-07-29 22:35:18 -04:00
|
|
|
func (sbc StackedBarChart) drawTitle(r Renderer) {
|
2019-02-13 21:55:13 -05:00
|
|
|
if len(sbc.Title) > 0 && !sbc.TitleStyle.Hidden {
|
2017-06-11 14:38:10 -04:00
|
|
|
r.SetFont(sbc.TitleStyle.GetFont(sbc.GetFont()))
|
|
|
|
r.SetFontColor(sbc.TitleStyle.GetFontColor(sbc.GetColorPalette().TextColor()))
|
|
|
|
titleFontSize := sbc.TitleStyle.GetFontSize(DefaultTitleFontSize)
|
|
|
|
r.SetFontSize(titleFontSize)
|
|
|
|
|
|
|
|
textBox := r.MeasureText(sbc.Title)
|
|
|
|
|
|
|
|
textWidth := textBox.Width()
|
|
|
|
textHeight := textBox.Height()
|
|
|
|
|
|
|
|
titleX := (sbc.GetWidth() >> 1) - (textWidth >> 1)
|
|
|
|
titleY := sbc.TitleStyle.Padding.GetTop(DefaultTitleTop) + textHeight
|
|
|
|
|
|
|
|
r.Text(sbc.Title, titleX, titleY)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (sbc StackedBarChart) getCanvasStyle() Style {
|
|
|
|
return sbc.Canvas.InheritFrom(sbc.styleDefaultsCanvas())
|
|
|
|
}
|
|
|
|
|
|
|
|
func (sbc StackedBarChart) styleDefaultsCanvas() Style {
|
|
|
|
return Style{
|
|
|
|
FillColor: sbc.GetColorPalette().CanvasColor(),
|
|
|
|
StrokeColor: sbc.GetColorPalette().CanvasStrokeColor(),
|
|
|
|
StrokeWidth: DefaultCanvasStrokeWidth,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetColorPalette returns the color palette for the chart.
|
|
|
|
func (sbc StackedBarChart) GetColorPalette() ColorPalette {
|
|
|
|
if sbc.ColorPalette != nil {
|
|
|
|
return sbc.ColorPalette
|
2016-07-28 21:51:55 -04:00
|
|
|
}
|
2017-06-11 14:38:10 -04:00
|
|
|
return AlternateColorPalette
|
2016-07-28 21:51:55 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
func (sbc StackedBarChart) getDefaultCanvasBox() Box {
|
|
|
|
return sbc.Box()
|
|
|
|
}
|
|
|
|
|
2016-07-30 12:35:44 -04:00
|
|
|
func (sbc StackedBarChart) getAdjustedCanvasBox(r Renderer, canvasBox Box) Box {
|
2016-07-28 21:51:55 -04:00
|
|
|
var totalWidth int
|
2016-07-30 02:13:33 -04:00
|
|
|
for _, bar := range sbc.Bars {
|
|
|
|
totalWidth += bar.GetWidth() + sbc.GetBarSpacing()
|
2016-07-28 21:51:55 -04:00
|
|
|
}
|
|
|
|
|
2019-02-13 21:55:13 -05:00
|
|
|
if !sbc.XAxis.Hidden {
|
2016-07-30 12:35:44 -04:00
|
|
|
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)
|
|
|
|
|
2019-02-13 19:09:26 -05:00
|
|
|
xaxisHeight = MaxInt(linesBox.Height()+(2*DefaultXAxisMargin), xaxisHeight)
|
2016-07-30 12:35:44 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return Box{
|
|
|
|
Top: canvasBox.Top,
|
|
|
|
Left: canvasBox.Left,
|
|
|
|
Right: canvasBox.Left + totalWidth,
|
|
|
|
Bottom: sbc.GetHeight() - xaxisHeight,
|
|
|
|
}
|
|
|
|
}
|
2016-07-29 22:35:18 -04:00
|
|
|
return Box{
|
2016-07-28 21:51:55 -04:00
|
|
|
Top: canvasBox.Top,
|
|
|
|
Left: canvasBox.Left,
|
|
|
|
Right: canvasBox.Left + totalWidth,
|
|
|
|
Bottom: canvasBox.Bottom,
|
2016-07-29 22:35:18 -04:00
|
|
|
}
|
2016-07-28 21:51:55 -04:00
|
|
|
}
|
|
|
|
|
2019-12-06 14:22:51 -05:00
|
|
|
func (sbc StackedBarChart) getHorizontalAdjustedCanvasBox(r Renderer, canvasBox Box) Box {
|
|
|
|
var totalHeight int
|
|
|
|
for _, bar := range sbc.Bars {
|
|
|
|
totalHeight += bar.GetWidth() + sbc.GetBarSpacing()
|
|
|
|
}
|
|
|
|
|
|
|
|
if !sbc.YAxis.Hidden {
|
|
|
|
yAxisWidth := DefaultHorizontalTickWidth
|
|
|
|
|
|
|
|
axisStyle := sbc.YAxis.InheritFrom(sbc.styleDefaultsHorizontalAxes())
|
|
|
|
axisStyle.WriteToRenderer(r)
|
|
|
|
|
|
|
|
cursor := canvasBox.Top
|
|
|
|
for _, bar := range sbc.Bars {
|
|
|
|
if len(bar.Name) > 0 {
|
|
|
|
barLabelBox := Box{
|
|
|
|
Top: cursor,
|
|
|
|
Left: 0,
|
|
|
|
Right: canvasBox.Left + DefaultYAxisMargin,
|
|
|
|
Bottom: cursor + bar.GetWidth() + sbc.GetBarSpacing(),
|
|
|
|
}
|
|
|
|
lines := Text.WrapFit(r, bar.Name, barLabelBox.Width(), axisStyle)
|
|
|
|
linesBox := Text.MeasureLines(r, lines, axisStyle)
|
|
|
|
|
|
|
|
yAxisWidth = MaxInt(linesBox.Height()+(2*DefaultXAxisMargin), yAxisWidth)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return Box{
|
|
|
|
Top: canvasBox.Top,
|
|
|
|
Left: canvasBox.Left + yAxisWidth,
|
|
|
|
Right: canvasBox.Right,
|
|
|
|
Bottom: canvasBox.Top + totalHeight,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return Box{
|
|
|
|
Top: canvasBox.Top,
|
|
|
|
Left: canvasBox.Left,
|
|
|
|
Right: canvasBox.Right,
|
|
|
|
Bottom: canvasBox.Top + totalHeight,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-28 21:51:55 -04:00
|
|
|
// Box returns the chart bounds as a box.
|
|
|
|
func (sbc StackedBarChart) Box() Box {
|
2016-07-29 22:35:18 -04:00
|
|
|
dpr := sbc.Background.Padding.GetRight(10)
|
|
|
|
dpb := sbc.Background.Padding.GetBottom(50)
|
2016-07-28 21:51:55 -04:00
|
|
|
|
|
|
|
return Box{
|
2017-06-11 14:38:10 -04:00
|
|
|
Top: sbc.Background.Padding.GetTop(20),
|
|
|
|
Left: sbc.Background.Padding.GetLeft(20),
|
2016-07-28 21:51:55 -04:00
|
|
|
Right: sbc.GetWidth() - dpr,
|
|
|
|
Bottom: sbc.GetHeight() - dpb,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (sbc StackedBarChart) styleDefaultsStackedBarValue(index int) Style {
|
|
|
|
return Style{
|
2017-06-11 14:38:10 -04:00
|
|
|
StrokeColor: sbc.GetColorPalette().GetSeriesColor(index),
|
2016-07-28 21:51:55 -04:00
|
|
|
StrokeWidth: 3.0,
|
2017-06-11 14:38:10 -04:00
|
|
|
FillColor: sbc.GetColorPalette().GetSeriesColor(index),
|
2019-12-06 14:22:51 -05:00
|
|
|
FontSize: sbc.getScaledFontSize(),
|
|
|
|
FontColor: sbc.GetColorPalette().TextColor(),
|
|
|
|
Font: sbc.GetFont(),
|
2016-07-28 21:51:55 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-29 22:35:18 -04:00
|
|
|
func (sbc StackedBarChart) styleDefaultsTitle() Style {
|
|
|
|
return sbc.TitleStyle.InheritFrom(Style{
|
|
|
|
FontColor: DefaultTextColor,
|
|
|
|
Font: sbc.GetFont(),
|
|
|
|
FontSize: sbc.getTitleFontSize(),
|
|
|
|
TextHorizontalAlign: TextHorizontalAlignCenter,
|
|
|
|
TextVerticalAlign: TextVerticalAlignTop,
|
|
|
|
TextWrap: TextWrapWord,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-12-06 14:22:51 -05:00
|
|
|
func (sbc StackedBarChart) getScaledFontSize() float64 {
|
|
|
|
effectiveDimension := MinInt(sbc.GetWidth(), sbc.GetHeight())
|
|
|
|
if effectiveDimension >= 2048 {
|
|
|
|
return 48.0
|
|
|
|
} else if effectiveDimension >= 1024 {
|
|
|
|
return 24.0
|
|
|
|
} else if effectiveDimension > 512 {
|
|
|
|
return 18.0
|
|
|
|
} else if effectiveDimension > 256 {
|
|
|
|
return 12.0
|
|
|
|
}
|
|
|
|
return 10.0
|
|
|
|
}
|
|
|
|
|
2016-07-29 22:35:18 -04:00
|
|
|
func (sbc StackedBarChart) getTitleFontSize() float64 {
|
2019-02-13 19:09:26 -05:00
|
|
|
effectiveDimension := MinInt(sbc.GetWidth(), sbc.GetHeight())
|
2016-07-29 22:35:18 -04:00
|
|
|
if effectiveDimension >= 2048 {
|
|
|
|
return 48
|
|
|
|
} else if effectiveDimension >= 1024 {
|
|
|
|
return 24
|
|
|
|
} else if effectiveDimension >= 512 {
|
|
|
|
return 18
|
|
|
|
} else if effectiveDimension >= 256 {
|
|
|
|
return 12
|
|
|
|
}
|
|
|
|
return 10
|
|
|
|
}
|
|
|
|
|
|
|
|
func (sbc StackedBarChart) styleDefaultsAxes() Style {
|
|
|
|
return Style{
|
|
|
|
StrokeColor: DefaultAxisColor,
|
|
|
|
Font: sbc.GetFont(),
|
|
|
|
FontSize: DefaultAxisFontSize,
|
|
|
|
FontColor: DefaultAxisColor,
|
|
|
|
TextHorizontalAlign: TextHorizontalAlignCenter,
|
|
|
|
TextVerticalAlign: TextVerticalAlignTop,
|
|
|
|
TextWrap: TextWrapWord,
|
|
|
|
}
|
|
|
|
}
|
2019-12-06 14:22:51 -05:00
|
|
|
|
|
|
|
func (sbc StackedBarChart) styleDefaultsHorizontalAxes() Style {
|
|
|
|
return Style{
|
|
|
|
StrokeColor: DefaultAxisColor,
|
|
|
|
Font: sbc.GetFont(),
|
|
|
|
FontSize: DefaultAxisFontSize,
|
|
|
|
FontColor: DefaultAxisColor,
|
|
|
|
TextHorizontalAlign: TextHorizontalAlignCenter,
|
|
|
|
TextVerticalAlign: TextVerticalAlignMiddle,
|
|
|
|
TextWrap: TextWrapWord,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-28 21:51:55 -04:00
|
|
|
func (sbc StackedBarChart) styleDefaultsElements() Style {
|
|
|
|
return Style{
|
|
|
|
Font: sbc.GetFont(),
|
|
|
|
}
|
|
|
|
}
|