checkin
This commit is contained in:
parent
a0ea012903
commit
8721033046
2 changed files with 140 additions and 40 deletions
|
@ -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,
|
||||||
|
|
|
@ -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.
|
||||||
|
|
Loading…
Reference in a new issue