merging master
This commit is contained in:
commit
2d5aeaf824
8 changed files with 494 additions and 17 deletions
|
@ -18,7 +18,8 @@ func inlineSVGWithClasses(res http.ResponseWriter, req *http.Request) {
|
||||||
"<body>"))
|
"<body>"))
|
||||||
|
|
||||||
pie := chart.PieChart{
|
pie := chart.PieChart{
|
||||||
// Note that setting ClassName will cause all other inline styles to be dropped!
|
// Notes: * Setting ClassName will cause all other inline styles to be dropped!
|
||||||
|
// * The following type classes may be added additionally: stroke, fill, text
|
||||||
Background: chart.Style{ClassName: "background"},
|
Background: chart.Style{ClassName: "background"},
|
||||||
Canvas: chart.Style{
|
Canvas: chart.Style{
|
||||||
ClassName: "canvas",
|
ClassName: "canvas",
|
||||||
|
@ -43,12 +44,12 @@ func css(res http.ResponseWriter, req *http.Request) {
|
||||||
res.Header().Set("Content-Type", "text/css")
|
res.Header().Set("Content-Type", "text/css")
|
||||||
res.Write([]byte("svg .background { fill: white; }" +
|
res.Write([]byte("svg .background { fill: white; }" +
|
||||||
"svg .canvas { fill: white; }" +
|
"svg .canvas { fill: white; }" +
|
||||||
"svg path.blue { fill: blue; stroke: lightblue; }" +
|
"svg .blue.fill.stroke { fill: blue; stroke: lightblue; }" +
|
||||||
"svg path.green { fill: green; stroke: lightgreen; }" +
|
"svg .green.fill.stroke { fill: green; stroke: lightgreen; }" +
|
||||||
"svg path.gray { fill: gray; stroke: lightgray; }" +
|
"svg .gray.fill.stroke { fill: gray; stroke: lightgray; }" +
|
||||||
"svg text.blue { fill: white; }" +
|
"svg .blue.text { fill: white; }" +
|
||||||
"svg text.green { fill: white; }" +
|
"svg .green.text { fill: white; }" +
|
||||||
"svg text.gray { fill: white; }"))
|
"svg .gray.text { fill: white; }"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
|
@ -5,17 +5,17 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
chart "github.com/wcharczuk/go-chart"
|
"github.com/wcharczuk/go-chart"
|
||||||
)
|
)
|
||||||
|
|
||||||
const style = "svg .background { fill: white; }" +
|
const style = "svg .background { fill: white; }" +
|
||||||
"svg .canvas { fill: white; }" +
|
"svg .canvas { fill: white; }" +
|
||||||
"svg path.blue { fill: blue; stroke: lightblue; }" +
|
"svg .blue.fill.stroke { fill: blue; stroke: lightblue; }" +
|
||||||
"svg path.green { fill: green; stroke: lightgreen; }" +
|
"svg .green.fill.stroke { fill: green; stroke: lightgreen; }" +
|
||||||
"svg path.gray { fill: gray; stroke: lightgray; }" +
|
"svg .gray.fill.stroke { fill: gray; stroke: lightgray; }" +
|
||||||
"svg text.blue { fill: white; }" +
|
"svg .blue.text { fill: white; }" +
|
||||||
"svg text.green { fill: white; }" +
|
"svg .green.text { fill: white; }" +
|
||||||
"svg text.gray { fill: white; }"
|
"svg .gray.text { fill: white; }"
|
||||||
|
|
||||||
func svgWithCustomInlineCSS(res http.ResponseWriter, _ *http.Request) {
|
func svgWithCustomInlineCSS(res http.ResponseWriter, _ *http.Request) {
|
||||||
res.Header().Set("Content-Type", chart.ContentTypeSVG)
|
res.Header().Set("Content-Type", chart.ContentTypeSVG)
|
||||||
|
|
54
_examples/donut_chart/main.go
Normal file
54
_examples/donut_chart/main.go
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/wcharczuk/go-chart"
|
||||||
|
)
|
||||||
|
|
||||||
|
func drawChart(res http.ResponseWriter, req *http.Request) {
|
||||||
|
pie := chart.DonutChart{
|
||||||
|
Width: 512,
|
||||||
|
Height: 512,
|
||||||
|
Values: []chart.Value{
|
||||||
|
{Value: 5, Label: "Blue"},
|
||||||
|
{Value: 5, Label: "Green"},
|
||||||
|
{Value: 4, Label: "Gray"},
|
||||||
|
{Value: 4, Label: "Orange"},
|
||||||
|
{Value: 3, Label: "Deep Blue"},
|
||||||
|
{Value: 3, Label: "test"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
res.Header().Set("Content-Type", "image/png")
|
||||||
|
err := pie.Render(chart.PNG, res)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error rendering pie chart: %v\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func drawChartRegression(res http.ResponseWriter, req *http.Request) {
|
||||||
|
pie := chart.DonutChart{
|
||||||
|
Width: 512,
|
||||||
|
Height: 512,
|
||||||
|
Values: []chart.Value{
|
||||||
|
{Value: 5, Label: "Blue"},
|
||||||
|
{Value: 2, Label: "Two"},
|
||||||
|
{Value: 1, Label: "One"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
res.Header().Set("Content-Type", chart.ContentTypeSVG)
|
||||||
|
err := pie.Render(chart.SVG, res)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error rendering pie chart: %v\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
http.HandleFunc("/", drawChart)
|
||||||
|
http.HandleFunc("/reg", drawChartRegression)
|
||||||
|
log.Fatal(http.ListenAndServe(":8080", nil))
|
||||||
|
}
|
BIN
_examples/donut_chart/output.png
Normal file
BIN
_examples/donut_chart/output.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
25
_examples/donut_chart/reg.svg
Normal file
25
_examples/donut_chart/reg.svg
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="512" height="512">\n<path d="M 0 0
|
||||||
|
L 512 0
|
||||||
|
L 512 512
|
||||||
|
L 0 512
|
||||||
|
L 0 0" style="stroke-width:0;stroke:rgba(255,255,255,1.0);fill:rgba(255,255,255,1.0)"/><path d="M 5 5
|
||||||
|
L 507 5
|
||||||
|
L 507 507
|
||||||
|
L 5 507
|
||||||
|
L 5 5" style="stroke-width:0;stroke:rgba(255,255,255,1.0);fill:rgba(255,255,255,1.0)"/><path d="M 256 256
|
||||||
|
L 438 256
|
||||||
|
A 182 182 225.00 1 1 127 127
|
||||||
|
L 256 256
|
||||||
|
Z" style="stroke-width:4;stroke:rgba(255,255,255,1.0);fill:rgba(106,195,203,1.0)"/><path d="M 256 256
|
||||||
|
L 127 127
|
||||||
|
A 182 182 90.00 0 1 385 127
|
||||||
|
L 256 256
|
||||||
|
Z" style="stroke-width:4;stroke:rgba(255,255,255,1.0);fill:rgba(42,190,137,1.0)"/><path d="M 256 256
|
||||||
|
L 385 127
|
||||||
|
A 182 182 45.00 0 1 438 256
|
||||||
|
L 256 256
|
||||||
|
Z" style="stroke-width:4;stroke:rgba(255,255,255,1.0);fill:rgba(110,128,139,1.0)"/><path d="M 256 256
|
||||||
|
L 321 256
|
||||||
|
A 65 65 359.00 1 1 321 255
|
||||||
|
L 256 256
|
||||||
|
Z" style="stroke-width:4;stroke:rgba(255,255,255,1.0);fill:rgba(255,255,255,1.0)"/><text x="159" y="461" style="stroke-width:0;stroke:none;fill:rgba(51,51,51,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif">Blue</text><text x="241" y="48" style="stroke-width:0;stroke:none;fill:rgba(51,51,51,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif">Two</text><text x="440" y="181" style="stroke-width:0;stroke:none;fill:rgba(51,51,51,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif">One</text></svg>
|
After Width: | Height: | Size: 1.4 KiB |
315
donut_chart.go
Normal file
315
donut_chart.go
Normal file
|
@ -0,0 +1,315 @@
|
||||||
|
package chart
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/golang/freetype/truetype"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DonutChart is a chart that draws sections of a circle based on percentages with an hole.
|
||||||
|
type DonutChart struct {
|
||||||
|
Title string
|
||||||
|
TitleStyle Style
|
||||||
|
|
||||||
|
ColorPalette ColorPalette
|
||||||
|
|
||||||
|
Width int
|
||||||
|
Height int
|
||||||
|
DPI float64
|
||||||
|
|
||||||
|
Background Style
|
||||||
|
Canvas Style
|
||||||
|
SliceStyle Style
|
||||||
|
|
||||||
|
Font *truetype.Font
|
||||||
|
defaultFont *truetype.Font
|
||||||
|
|
||||||
|
Values []Value
|
||||||
|
Elements []Renderable
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDPI returns the dpi for the chart.
|
||||||
|
func (pc DonutChart) GetDPI(defaults ...float64) float64 {
|
||||||
|
if pc.DPI == 0 {
|
||||||
|
if len(defaults) > 0 {
|
||||||
|
return defaults[0]
|
||||||
|
}
|
||||||
|
return DefaultDPI
|
||||||
|
}
|
||||||
|
return pc.DPI
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFont returns the text font.
|
||||||
|
func (pc DonutChart) GetFont() *truetype.Font {
|
||||||
|
if pc.Font == nil {
|
||||||
|
return pc.defaultFont
|
||||||
|
}
|
||||||
|
return pc.Font
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetWidth returns the chart width or the default value.
|
||||||
|
func (pc DonutChart) GetWidth() int {
|
||||||
|
if pc.Width == 0 {
|
||||||
|
return DefaultChartWidth
|
||||||
|
}
|
||||||
|
return pc.Width
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHeight returns the chart height or the default value.
|
||||||
|
func (pc DonutChart) GetHeight() int {
|
||||||
|
if pc.Height == 0 {
|
||||||
|
return DefaultChartWidth
|
||||||
|
}
|
||||||
|
return pc.Height
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render renders the chart with the given renderer to the given io.Writer.
|
||||||
|
func (pc DonutChart) Render(rp RendererProvider, w io.Writer) error {
|
||||||
|
if len(pc.Values) == 0 {
|
||||||
|
return errors.New("please provide at least one value")
|
||||||
|
}
|
||||||
|
|
||||||
|
r, err := rp(pc.GetWidth(), pc.GetHeight())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if pc.Font == nil {
|
||||||
|
defaultFont, err := GetDefaultFont()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pc.defaultFont = defaultFont
|
||||||
|
}
|
||||||
|
r.SetDPI(pc.GetDPI(DefaultDPI))
|
||||||
|
|
||||||
|
canvasBox := pc.getDefaultCanvasBox()
|
||||||
|
canvasBox = pc.getCircleAdjustedCanvasBox(canvasBox)
|
||||||
|
|
||||||
|
pc.drawBackground(r)
|
||||||
|
pc.drawCanvas(r, canvasBox)
|
||||||
|
|
||||||
|
finalValues, err := pc.finalizeValues(pc.Values)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pc.drawSlices(r, canvasBox, finalValues)
|
||||||
|
pc.drawTitle(r)
|
||||||
|
for _, a := range pc.Elements {
|
||||||
|
a(r, canvasBox, pc.styleDefaultsElements())
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.Save(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pc DonutChart) drawBackground(r Renderer) {
|
||||||
|
Draw.Box(r, Box{
|
||||||
|
Right: pc.GetWidth(),
|
||||||
|
Bottom: pc.GetHeight(),
|
||||||
|
}, pc.getBackgroundStyle())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pc DonutChart) drawCanvas(r Renderer, canvasBox Box) {
|
||||||
|
Draw.Box(r, canvasBox, pc.getCanvasStyle())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pc DonutChart) drawTitle(r Renderer) {
|
||||||
|
if len(pc.Title) > 0 && !pc.TitleStyle.Hidden {
|
||||||
|
Draw.TextWithin(r, pc.Title, pc.Box(), pc.styleDefaultsTitle())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pc DonutChart) drawSlices(r Renderer, canvasBox Box, values []Value) {
|
||||||
|
cx, cy := canvasBox.Center()
|
||||||
|
diameter := MinInt(canvasBox.Width(), canvasBox.Height())
|
||||||
|
radius := float64(diameter>>1) / 1.1
|
||||||
|
labelRadius := (radius * 2.83) / 3.0
|
||||||
|
|
||||||
|
// draw the donut slices
|
||||||
|
var rads, delta, delta2, total float64
|
||||||
|
var lx, ly int
|
||||||
|
|
||||||
|
if len(values) == 1 {
|
||||||
|
pc.styleDonutChartValue(0).WriteToRenderer(r)
|
||||||
|
r.MoveTo(cx, cy)
|
||||||
|
r.Circle(radius, cx, cy)
|
||||||
|
} else {
|
||||||
|
for index, v := range values {
|
||||||
|
v.Style.InheritFrom(pc.styleDonutChartValue(index)).WriteToRenderer(r)
|
||||||
|
r.MoveTo(cx, cy)
|
||||||
|
rads = PercentToRadians(total)
|
||||||
|
delta = PercentToRadians(v.Value)
|
||||||
|
|
||||||
|
r.ArcTo(cx, cy, (radius / 1.25), (radius / 1.25), rads, delta)
|
||||||
|
|
||||||
|
r.LineTo(cx, cy)
|
||||||
|
r.Close()
|
||||||
|
r.FillStroke()
|
||||||
|
total = total + v.Value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//making the donut hole
|
||||||
|
v := Value{Value: 100, Label: "center"}
|
||||||
|
styletemp := pc.SliceStyle.InheritFrom(Style{
|
||||||
|
StrokeColor: ColorWhite, StrokeWidth: 4.0, FillColor: ColorWhite, FontColor: ColorWhite, //Font: pc.GetFont(),//FontSize: pc.getScaledFontSize(),
|
||||||
|
})
|
||||||
|
v.Style.InheritFrom(styletemp).WriteToRenderer(r)
|
||||||
|
r.MoveTo(cx, cy)
|
||||||
|
r.ArcTo(cx, cy, (radius / 3.5), (radius / 3.5), DegreesToRadians(0), DegreesToRadians(359))
|
||||||
|
r.LineTo(cx, cy)
|
||||||
|
r.Close()
|
||||||
|
r.FillStroke()
|
||||||
|
|
||||||
|
// draw the labels
|
||||||
|
total = 0
|
||||||
|
for index, v := range values {
|
||||||
|
v.Style.InheritFrom(pc.styleDonutChartValue(index)).WriteToRenderer(r)
|
||||||
|
if len(v.Label) > 0 {
|
||||||
|
delta2 = PercentToRadians(total + (v.Value / 2.0))
|
||||||
|
delta2 = RadianAdd(delta2, _pi2)
|
||||||
|
lx, ly = CirclePoint(cx, cy, labelRadius, delta2)
|
||||||
|
|
||||||
|
tb := r.MeasureText(v.Label)
|
||||||
|
lx = lx - (tb.Width() >> 1)
|
||||||
|
ly = ly + (tb.Height() >> 1)
|
||||||
|
|
||||||
|
r.Text(v.Label, lx, ly)
|
||||||
|
}
|
||||||
|
total = total + v.Value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pc DonutChart) finalizeValues(values []Value) ([]Value, error) {
|
||||||
|
finalValues := Values(values).Normalize()
|
||||||
|
if len(finalValues) == 0 {
|
||||||
|
return nil, fmt.Errorf("donut chart must contain at least (1) non-zero value")
|
||||||
|
}
|
||||||
|
return finalValues, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pc DonutChart) getDefaultCanvasBox() Box {
|
||||||
|
return pc.Box()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pc DonutChart) getCircleAdjustedCanvasBox(canvasBox Box) Box {
|
||||||
|
circleDiameter := MinInt(canvasBox.Width(), canvasBox.Height())
|
||||||
|
|
||||||
|
square := Box{
|
||||||
|
Right: circleDiameter,
|
||||||
|
Bottom: circleDiameter,
|
||||||
|
}
|
||||||
|
|
||||||
|
return canvasBox.Fit(square)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pc DonutChart) getBackgroundStyle() Style {
|
||||||
|
return pc.Background.InheritFrom(pc.styleDefaultsBackground())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pc DonutChart) getCanvasStyle() Style {
|
||||||
|
return pc.Canvas.InheritFrom(pc.styleDefaultsCanvas())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pc DonutChart) styleDefaultsCanvas() Style {
|
||||||
|
return Style{
|
||||||
|
FillColor: pc.GetColorPalette().CanvasColor(),
|
||||||
|
StrokeColor: pc.GetColorPalette().CanvasStrokeColor(),
|
||||||
|
StrokeWidth: DefaultStrokeWidth,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pc DonutChart) styleDefaultsDonutChartValue() Style {
|
||||||
|
return Style{
|
||||||
|
StrokeColor: pc.GetColorPalette().TextColor(),
|
||||||
|
StrokeWidth: 4.0,
|
||||||
|
FillColor: pc.GetColorPalette().TextColor(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pc DonutChart) styleDonutChartValue(index int) Style {
|
||||||
|
return pc.SliceStyle.InheritFrom(Style{
|
||||||
|
StrokeColor: ColorWhite,
|
||||||
|
StrokeWidth: 4.0,
|
||||||
|
FillColor: pc.GetColorPalette().GetSeriesColor(index),
|
||||||
|
FontSize: pc.getScaledFontSize(),
|
||||||
|
FontColor: pc.GetColorPalette().TextColor(),
|
||||||
|
Font: pc.GetFont(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pc DonutChart) getScaledFontSize() float64 {
|
||||||
|
effectiveDimension := MinInt(pc.GetWidth(), pc.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
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pc DonutChart) styleDefaultsBackground() Style {
|
||||||
|
return Style{
|
||||||
|
FillColor: pc.GetColorPalette().BackgroundColor(),
|
||||||
|
StrokeColor: pc.GetColorPalette().BackgroundStrokeColor(),
|
||||||
|
StrokeWidth: DefaultStrokeWidth,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pc DonutChart) styleDefaultsElements() Style {
|
||||||
|
return Style{
|
||||||
|
Font: pc.GetFont(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pc DonutChart) styleDefaultsTitle() Style {
|
||||||
|
return pc.TitleStyle.InheritFrom(Style{
|
||||||
|
FontColor: pc.GetColorPalette().TextColor(),
|
||||||
|
Font: pc.GetFont(),
|
||||||
|
FontSize: pc.getTitleFontSize(),
|
||||||
|
TextHorizontalAlign: TextHorizontalAlignCenter,
|
||||||
|
TextVerticalAlign: TextVerticalAlignTop,
|
||||||
|
TextWrap: TextWrapWord,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pc DonutChart) getTitleFontSize() float64 {
|
||||||
|
effectiveDimension := MinInt(pc.GetWidth(), pc.GetHeight())
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetColorPalette returns the color palette for the chart.
|
||||||
|
func (pc DonutChart) GetColorPalette() ColorPalette {
|
||||||
|
if pc.ColorPalette != nil {
|
||||||
|
return pc.ColorPalette
|
||||||
|
}
|
||||||
|
return AlternateColorPalette
|
||||||
|
}
|
||||||
|
|
||||||
|
// Box returns the chart bounds as a box.
|
||||||
|
func (pc DonutChart) Box() Box {
|
||||||
|
dpr := pc.Background.Padding.GetRight(DefaultBackgroundPadding.Right)
|
||||||
|
dpb := pc.Background.Padding.GetBottom(DefaultBackgroundPadding.Bottom)
|
||||||
|
|
||||||
|
return Box{
|
||||||
|
Top: pc.Background.Padding.GetTop(DefaultBackgroundPadding.Top),
|
||||||
|
Left: pc.Background.Padding.GetLeft(DefaultBackgroundPadding.Left),
|
||||||
|
Right: pc.GetWidth() - dpr,
|
||||||
|
Bottom: pc.GetHeight() - dpb,
|
||||||
|
}
|
||||||
|
}
|
69
donut_chart_test.go
Normal file
69
donut_chart_test.go
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
package chart
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/blend/go-sdk/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDonutChart(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
pie := DonutChart{
|
||||||
|
Canvas: Style{
|
||||||
|
FillColor: ColorLightGray,
|
||||||
|
},
|
||||||
|
Values: []Value{
|
||||||
|
{Value: 10, Label: "Blue"},
|
||||||
|
{Value: 9, Label: "Green"},
|
||||||
|
{Value: 8, Label: "Gray"},
|
||||||
|
{Value: 7, Label: "Orange"},
|
||||||
|
{Value: 6, Label: "HEANG"},
|
||||||
|
{Value: 5, Label: "??"},
|
||||||
|
{Value: 2, Label: "!!"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
b := bytes.NewBuffer([]byte{})
|
||||||
|
pie.Render(PNG, b)
|
||||||
|
assert.NotZero(b.Len())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDonutChartDropsZeroValues(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
pie := DonutChart{
|
||||||
|
Canvas: Style{
|
||||||
|
FillColor: ColorLightGray,
|
||||||
|
},
|
||||||
|
Values: []Value{
|
||||||
|
{Value: 5, Label: "Blue"},
|
||||||
|
{Value: 5, Label: "Green"},
|
||||||
|
{Value: 0, Label: "Gray"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
b := bytes.NewBuffer([]byte{})
|
||||||
|
err := pie.Render(PNG, b)
|
||||||
|
assert.Nil(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDonutChartAllZeroValues(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
pie := DonutChart{
|
||||||
|
Canvas: Style{
|
||||||
|
FillColor: ColorLightGray,
|
||||||
|
},
|
||||||
|
Values: []Value{
|
||||||
|
{Value: 0, Label: "Blue"},
|
||||||
|
{Value: 0, Label: "Green"},
|
||||||
|
{Value: 0, Label: "Gray"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
b := bytes.NewBuffer([]byte{})
|
||||||
|
err := pie.Render(PNG, b)
|
||||||
|
assert.NotNil(err)
|
||||||
|
}
|
|
@ -310,15 +310,28 @@ func (c *canvas) getFontFace(s Style) string {
|
||||||
|
|
||||||
// styleAsSVG returns the style as a svg style or class string.
|
// styleAsSVG returns the style as a svg style or class string.
|
||||||
func (c *canvas) styleAsSVG(s Style) string {
|
func (c *canvas) styleAsSVG(s Style) string {
|
||||||
if s.ClassName != "" {
|
|
||||||
return fmt.Sprintf("class=\"%s\"", s.ClassName)
|
|
||||||
}
|
|
||||||
sw := s.StrokeWidth
|
sw := s.StrokeWidth
|
||||||
sc := s.StrokeColor
|
sc := s.StrokeColor
|
||||||
fc := s.FillColor
|
fc := s.FillColor
|
||||||
fs := s.FontSize
|
fs := s.FontSize
|
||||||
fnc := s.FontColor
|
fnc := s.FontColor
|
||||||
|
|
||||||
|
if s.ClassName != "" {
|
||||||
|
var classes []string
|
||||||
|
classes = append(classes, s.ClassName)
|
||||||
|
if !sc.IsZero() {
|
||||||
|
classes = append(classes, "stroke")
|
||||||
|
}
|
||||||
|
if !fc.IsZero() {
|
||||||
|
classes = append(classes, "fill")
|
||||||
|
}
|
||||||
|
if fs != 0 || s.Font != nil {
|
||||||
|
classes = append(classes, "text")
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("class=\"%s\"", strings.Join(classes, " "))
|
||||||
|
}
|
||||||
|
|
||||||
var pieces []string
|
var pieces []string
|
||||||
|
|
||||||
if sw != 0 {
|
if sw != 0 {
|
||||||
|
|
Loading…
Reference in a new issue