pie charts!
This commit is contained in:
parent
ec4d92fc5e
commit
c17c9a4bb4
10 changed files with 485 additions and 45 deletions
122
defaults.go
122
defaults.go
|
@ -66,42 +66,85 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// DefaultBackgroundColor is the default chart background color.
|
// ColorWhite is white.
|
||||||
// It is equivalent to css color:white.
|
ColorWhite = drawing.Color{R: 255, G: 255, B: 255, A: 255}
|
||||||
DefaultBackgroundColor = drawing.Color{R: 255, G: 255, B: 255, A: 255}
|
// ColorBlue is the basic theme blue color.
|
||||||
// DefaultBackgroundStrokeColor is the default chart border color.
|
ColorBlue = drawing.Color{R: 0, G: 116, B: 217, A: 255}
|
||||||
// It is equivalent to color:white.
|
// ColorCyan is the basic theme cyan color.
|
||||||
DefaultBackgroundStrokeColor = drawing.Color{R: 255, G: 255, B: 255, A: 255}
|
ColorCyan = drawing.Color{R: 0, G: 217, B: 210, A: 255}
|
||||||
// DefaultCanvasColor is the default chart canvas color.
|
// ColorGreen is the basic theme green color.
|
||||||
// It is equivalent to css color:white.
|
ColorGreen = drawing.Color{R: 0, G: 217, B: 101, A: 255}
|
||||||
DefaultCanvasColor = drawing.Color{R: 255, G: 255, B: 255, A: 255}
|
// ColorRed is the basic theme red color.
|
||||||
// DefaultCanvasStrokeColor is the default chart canvas stroke color.
|
ColorRed = drawing.Color{R: 217, G: 0, B: 116, A: 255}
|
||||||
// It is equivalent to css color:white.
|
// ColorOrange is the basic theme orange color.
|
||||||
DefaultCanvasStrokeColor = drawing.Color{R: 255, G: 255, B: 255, A: 255}
|
ColorOrange = drawing.Color{R: 217, G: 101, B: 0, A: 255}
|
||||||
// DefaultTextColor is the default chart text color.
|
// ColorYellow is the basic theme yellow color.
|
||||||
// It is equivalent to #333333.
|
ColorYellow = drawing.Color{R: 217, G: 210, B: 0, A: 255}
|
||||||
DefaultTextColor = drawing.Color{R: 51, G: 51, B: 51, A: 255}
|
// ColorBlack is the basic theme black color.
|
||||||
// DefaultAxisColor is the default chart axis line color.
|
ColorBlack = drawing.Color{R: 51, G: 51, B: 51, A: 255}
|
||||||
// It is equivalent to #333333.
|
// ColorLightGray is the basic theme light gray color.
|
||||||
DefaultAxisColor = drawing.Color{R: 51, G: 51, B: 51, A: 255}
|
ColorLightGray = drawing.Color{R: 239, G: 239, B: 239, A: 255}
|
||||||
// DefaultStrokeColor is the default chart border color.
|
|
||||||
// It is equivalent to #efefef.
|
// ColorAlternateBlue is a alternate theme color.
|
||||||
DefaultStrokeColor = drawing.Color{R: 239, G: 239, B: 239, A: 255}
|
ColorAlternateBlue = drawing.Color{R: 106, G: 195, B: 203, A: 255}
|
||||||
// DefaultFillColor is the default fill color.
|
// ColorAlternateGreen is a alternate theme color.
|
||||||
// It is equivalent to #0074d9.
|
ColorAlternateGreen = drawing.Color{R: 42, G: 190, B: 137, A: 255}
|
||||||
DefaultFillColor = drawing.Color{R: 0, G: 217, B: 116, A: 255}
|
// ColorAlternateGray is a alternate theme color.
|
||||||
// DefaultAnnotationFillColor is the default annotation background color.
|
ColorAlternateGray = drawing.Color{R: 110, G: 128, B: 139, A: 255}
|
||||||
DefaultAnnotationFillColor = drawing.Color{R: 255, G: 255, B: 255, A: 255}
|
// ColorAlternateYellow is a alternate theme color.
|
||||||
// DefaultGridLineColor is the default grid line color.
|
ColorAlternateYellow = drawing.Color{R: 240, G: 174, B: 90, A: 255}
|
||||||
DefaultGridLineColor = drawing.Color{R: 239, G: 239, B: 239, A: 255}
|
// ColorAlternateLightGray is a alternate theme color.
|
||||||
|
ColorAlternateLightGray = drawing.Color{R: 187, G: 190, B: 191, A: 255}
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// DefaultSeriesStrokeColors are a couple default series colors.
|
// DefaultBackgroundColor is the default chart background color.
|
||||||
DefaultSeriesStrokeColors = []drawing.Color{
|
// It is equivalent to css color:white.
|
||||||
{R: 0, G: 116, B: 217, A: 255},
|
DefaultBackgroundColor = ColorWhite
|
||||||
{R: 0, G: 217, B: 116, A: 255},
|
// DefaultBackgroundStrokeColor is the default chart border color.
|
||||||
{R: 217, G: 0, B: 116, A: 255},
|
// It is equivalent to color:white.
|
||||||
|
DefaultBackgroundStrokeColor = ColorWhite
|
||||||
|
// DefaultCanvasColor is the default chart canvas color.
|
||||||
|
// It is equivalent to css color:white.
|
||||||
|
DefaultCanvasColor = ColorWhite
|
||||||
|
// DefaultCanvasStrokeColor is the default chart canvas stroke color.
|
||||||
|
// It is equivalent to css color:white.
|
||||||
|
DefaultCanvasStrokeColor = ColorWhite
|
||||||
|
// DefaultTextColor is the default chart text color.
|
||||||
|
// It is equivalent to #333333.
|
||||||
|
DefaultTextColor = ColorBlack
|
||||||
|
// DefaultAxisColor is the default chart axis line color.
|
||||||
|
// It is equivalent to #333333.
|
||||||
|
DefaultAxisColor = ColorBlack
|
||||||
|
// DefaultStrokeColor is the default chart border color.
|
||||||
|
// It is equivalent to #efefef.
|
||||||
|
DefaultStrokeColor = ColorLightGray
|
||||||
|
// DefaultFillColor is the default fill color.
|
||||||
|
// It is equivalent to #0074d9.
|
||||||
|
DefaultFillColor = ColorBlue
|
||||||
|
// DefaultAnnotationFillColor is the default annotation background color.
|
||||||
|
DefaultAnnotationFillColor = ColorWhite
|
||||||
|
// DefaultGridLineColor is the default grid line color.
|
||||||
|
DefaultGridLineColor = ColorLightGray
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// DefaultColors are a couple default series colors.
|
||||||
|
DefaultColors = []drawing.Color{
|
||||||
|
ColorBlue,
|
||||||
|
ColorGreen,
|
||||||
|
ColorRed,
|
||||||
|
ColorCyan,
|
||||||
|
ColorOrange,
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultAlternateColors are a couple alternate colors.
|
||||||
|
DefaultAlternateColors = []drawing.Color{
|
||||||
|
ColorAlternateBlue,
|
||||||
|
ColorAlternateGreen,
|
||||||
|
ColorAlternateGray,
|
||||||
|
ColorAlternateYellow,
|
||||||
|
ColorAlternateLightGray,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -117,10 +160,17 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetDefaultSeriesStrokeColor returns a color from the default list by index.
|
// GetDefaultSeriesStrokeColor returns a color from the default list by index.
|
||||||
// NOTE: the index will wrap around (using a modulo).g
|
// NOTE: the index will wrap around (using a modulo).
|
||||||
func GetDefaultSeriesStrokeColor(index int) drawing.Color {
|
func GetDefaultSeriesStrokeColor(index int) drawing.Color {
|
||||||
finalIndex := index % len(DefaultSeriesStrokeColors)
|
finalIndex := index % len(DefaultColors)
|
||||||
return DefaultSeriesStrokeColors[finalIndex]
|
return DefaultColors[finalIndex]
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDefaultPieChartValueColor returns a color from the default list by index.
|
||||||
|
// NOTE: the index will wrap around (using a modulo).
|
||||||
|
func GetDefaultPieChartValueColor(index int) drawing.Color {
|
||||||
|
finalIndex := index % len(DefaultAlternateColors)
|
||||||
|
return DefaultAlternateColors[finalIndex]
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
@ -101,10 +101,10 @@ func (p *Path) CubicCurveTo(cx1, cy1, cx2, cy2, x, y float64) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ArcTo adds an arc to the path
|
// ArcTo adds an arc to the path
|
||||||
func (p *Path) ArcTo(cx, cy, rx, ry, startAngle, angle float64) {
|
func (p *Path) ArcTo(cx, cy, rx, ry, startAngle, delta float64) {
|
||||||
endAngle := startAngle + angle
|
endAngle := startAngle + delta
|
||||||
clockWise := true
|
clockWise := true
|
||||||
if angle < 0 {
|
if delta < 0 {
|
||||||
clockWise = false
|
clockWise = false
|
||||||
}
|
}
|
||||||
// normalize
|
// normalize
|
||||||
|
@ -124,7 +124,7 @@ func (p *Path) ArcTo(cx, cy, rx, ry, startAngle, angle float64) {
|
||||||
} else {
|
} else {
|
||||||
p.MoveTo(startX, startY)
|
p.MoveTo(startX, startY)
|
||||||
}
|
}
|
||||||
p.appendToPath(ArcToComponent, cx, cy, rx, ry, startAngle, angle)
|
p.appendToPath(ArcToComponent, cx, cy, rx, ry, startAngle, delta)
|
||||||
p.x = cx + math.Cos(endAngle)*rx
|
p.x = cx + math.Cos(endAngle)*rx
|
||||||
p.y = cy + math.Sin(endAngle)*ry
|
p.y = cy + math.Sin(endAngle)*ry
|
||||||
}
|
}
|
||||||
|
|
|
@ -171,8 +171,8 @@ func (gc *StackGraphicContext) CubicCurveTo(cx1, cy1, cx2, cy2, x, y float64) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ArcTo draws an arc.
|
// ArcTo draws an arc.
|
||||||
func (gc *StackGraphicContext) ArcTo(cx, cy, rx, ry, startAngle, angle float64) {
|
func (gc *StackGraphicContext) ArcTo(cx, cy, rx, ry, startAngle, delta float64) {
|
||||||
gc.current.Path.ArcTo(cx, cy, rx, ry, startAngle, angle)
|
gc.current.Path.ArcTo(cx, cy, rx, ry, startAngle, delta)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close closes a path.
|
// Close closes a path.
|
||||||
|
|
35
examples/pie_chart/main.go
Normal file
35
examples/pie_chart/main.go
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/wcharczuk/go-chart"
|
||||||
|
)
|
||||||
|
|
||||||
|
func drawChart(res http.ResponseWriter, req *http.Request) {
|
||||||
|
pie := chart.PieChart{
|
||||||
|
Canvas: chart.Style{
|
||||||
|
FillColor: chart.ColorLightGray,
|
||||||
|
},
|
||||||
|
Values: []chart.PieChartValue{
|
||||||
|
{Value: 0.3, Label: "Blue"},
|
||||||
|
{Value: 0.2, Label: "Green"},
|
||||||
|
{Value: 0.2, Label: "Gray"},
|
||||||
|
{Value: 0.1, Label: "Orange"},
|
||||||
|
{Value: 0.1, Label: "??"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
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 main() {
|
||||||
|
http.HandleFunc("/", drawChart)
|
||||||
|
log.Fatal(http.ListenAndServe(":8080", nil))
|
||||||
|
}
|
284
pie_chart.go
Normal file
284
pie_chart.go
Normal file
|
@ -0,0 +1,284 @@
|
||||||
|
package chart
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
|
||||||
|
"github.com/golang/freetype/truetype"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PieChartValue is a slice of a pie-chart.
|
||||||
|
type PieChartValue struct {
|
||||||
|
Style Style
|
||||||
|
Label string
|
||||||
|
Value float64
|
||||||
|
}
|
||||||
|
|
||||||
|
// PieChart is a chart that draws sections of a circle based on percentages.
|
||||||
|
type PieChart struct {
|
||||||
|
Title string
|
||||||
|
TitleStyle Style
|
||||||
|
|
||||||
|
Width int
|
||||||
|
Height int
|
||||||
|
DPI float64
|
||||||
|
|
||||||
|
Background Style
|
||||||
|
Canvas Style
|
||||||
|
|
||||||
|
Font *truetype.Font
|
||||||
|
defaultFont *truetype.Font
|
||||||
|
|
||||||
|
Values []PieChartValue
|
||||||
|
Elements []Renderable
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDPI returns the dpi for the chart.
|
||||||
|
func (pc PieChart) 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 PieChart) GetFont() *truetype.Font {
|
||||||
|
if pc.Font == nil {
|
||||||
|
return pc.defaultFont
|
||||||
|
}
|
||||||
|
return pc.Font
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetWidth returns the chart width or the default value.
|
||||||
|
func (pc PieChart) GetWidth() int {
|
||||||
|
if pc.Width == 0 {
|
||||||
|
return DefaultChartWidth
|
||||||
|
}
|
||||||
|
return pc.Width
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHeight returns the chart height or the default value.
|
||||||
|
func (pc PieChart) 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 PieChart) 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)
|
||||||
|
|
||||||
|
valuesWithPlaceholder, err := pc.finalizeValues(pc.Values)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pc.drawSlices(r, canvasBox, valuesWithPlaceholder)
|
||||||
|
pc.drawTitle(r)
|
||||||
|
for _, a := range pc.Elements {
|
||||||
|
a(r, canvasBox, pc.styleDefaultsElements())
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.Save(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pc PieChart) drawBackground(r Renderer) {
|
||||||
|
DrawBox(r, Box{
|
||||||
|
Right: pc.GetWidth(),
|
||||||
|
Bottom: pc.GetHeight(),
|
||||||
|
}, pc.getBackgroundStyle())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pc PieChart) drawCanvas(r Renderer, canvasBox Box) {
|
||||||
|
DrawBox(r, canvasBox, pc.getCanvasStyle())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pc PieChart) drawTitle(r Renderer) {
|
||||||
|
if len(pc.Title) > 0 && pc.TitleStyle.Show {
|
||||||
|
r.SetFont(pc.TitleStyle.GetFont(pc.GetFont()))
|
||||||
|
r.SetFontColor(pc.TitleStyle.GetFontColor(DefaultTextColor))
|
||||||
|
titleFontSize := pc.TitleStyle.GetFontSize(DefaultTitleFontSize)
|
||||||
|
r.SetFontSize(titleFontSize)
|
||||||
|
|
||||||
|
textBox := r.MeasureText(pc.Title)
|
||||||
|
|
||||||
|
textWidth := textBox.Width()
|
||||||
|
textHeight := textBox.Height()
|
||||||
|
|
||||||
|
titleX := (pc.GetWidth() >> 1) - (textWidth >> 1)
|
||||||
|
titleY := pc.TitleStyle.Padding.GetTop(DefaultTitleTop) + textHeight
|
||||||
|
|
||||||
|
r.Text(pc.Title, titleX, titleY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pc PieChart) drawSlices(r Renderer, canvasBox Box, values []PieChartValue) {
|
||||||
|
cx, cy := canvasBox.Center()
|
||||||
|
diameter := MinInt(canvasBox.Width(), canvasBox.Height())
|
||||||
|
radius := float64(diameter >> 1)
|
||||||
|
radius2 := (radius * 2.0) / 3.0
|
||||||
|
|
||||||
|
var rads, delta, delta2, total float64
|
||||||
|
var lx, ly int
|
||||||
|
for index, v := range values {
|
||||||
|
v.Style.InheritFrom(pc.stylePieChartValue(index)).PersistToRenderer(r)
|
||||||
|
r.MoveTo(cx, cy)
|
||||||
|
|
||||||
|
rads = PercentToRadians(total)
|
||||||
|
delta = PercentToRadians(v.Value)
|
||||||
|
|
||||||
|
r.ArcTo(cx, cy, radius, radius, rads, delta)
|
||||||
|
|
||||||
|
r.LineTo(cx, cy)
|
||||||
|
r.Close()
|
||||||
|
r.FillStroke()
|
||||||
|
total = total + v.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
total = 0
|
||||||
|
for index, v := range values {
|
||||||
|
v.Style.InheritFrom(pc.stylePieChartValue(index)).PersistToRenderer(r)
|
||||||
|
if len(v.Label) > 0 {
|
||||||
|
delta2 = RadianAdd(PercentToRadians(total+(v.Value/2.0)), _pi2)
|
||||||
|
lx = cx + int(radius2*math.Sin(delta2))
|
||||||
|
ly = cy - int(radius2*math.Cos(delta2))
|
||||||
|
|
||||||
|
tb := r.MeasureText(v.Label)
|
||||||
|
lx = lx - (tb.Width() >> 1)
|
||||||
|
|
||||||
|
r.Text(v.Label, lx, ly)
|
||||||
|
}
|
||||||
|
total = total + v.Value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pc PieChart) finalizeValues(values []PieChartValue) ([]PieChartValue, error) {
|
||||||
|
var total float64
|
||||||
|
for _, v := range values {
|
||||||
|
total += v.Value
|
||||||
|
if total > 1.0 {
|
||||||
|
return nil, errors.New("Values total exceeded 1.0; please normalize pie chart values to [0,1.0)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
remainder := 1.0 - total
|
||||||
|
if RoundDown(remainder, 0.0001) > 0 {
|
||||||
|
return append(values, PieChartValue{
|
||||||
|
Style: pc.styleDefaultsPieChartValue(),
|
||||||
|
Value: remainder,
|
||||||
|
}), nil
|
||||||
|
}
|
||||||
|
return values, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pc PieChart) getDefaultCanvasBox() Box {
|
||||||
|
return pc.Box()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pc PieChart) getCircleAdjustedCanvasBox(canvasBox Box) Box {
|
||||||
|
circleDiameter := MinInt(canvasBox.Width(), canvasBox.Height())
|
||||||
|
|
||||||
|
square := Box{
|
||||||
|
Right: circleDiameter,
|
||||||
|
Bottom: circleDiameter,
|
||||||
|
}
|
||||||
|
|
||||||
|
return canvasBox.Fit(square)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pc PieChart) getBackgroundStyle() Style {
|
||||||
|
return pc.Background.InheritFrom(pc.styleDefaultsBackground())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pc PieChart) getCanvasStyle() Style {
|
||||||
|
return pc.Canvas.InheritFrom(pc.styleDefaultsCanvas())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pc PieChart) styleDefaultsCanvas() Style {
|
||||||
|
return Style{
|
||||||
|
FillColor: DefaultCanvasColor,
|
||||||
|
StrokeColor: DefaultCanvasStrokeColor,
|
||||||
|
StrokeWidth: DefaultStrokeWidth,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pc PieChart) styleDefaultsPieChartValue() Style {
|
||||||
|
return Style{
|
||||||
|
StrokeColor: ColorWhite,
|
||||||
|
StrokeWidth: 5.0,
|
||||||
|
FillColor: ColorWhite,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pc PieChart) stylePieChartValue(index int) Style {
|
||||||
|
return Style{
|
||||||
|
StrokeColor: ColorWhite,
|
||||||
|
StrokeWidth: 5.0,
|
||||||
|
FillColor: GetDefaultPieChartValueColor(index),
|
||||||
|
FontSize: 24.0,
|
||||||
|
FontColor: ColorWhite,
|
||||||
|
Font: pc.GetFont(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pc PieChart) styleDefaultsBackground() Style {
|
||||||
|
return Style{
|
||||||
|
FillColor: DefaultBackgroundColor,
|
||||||
|
StrokeColor: DefaultBackgroundStrokeColor,
|
||||||
|
StrokeWidth: DefaultStrokeWidth,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pc PieChart) styleDefaultsSeries(seriesIndex int) Style {
|
||||||
|
strokeColor := GetDefaultSeriesStrokeColor(seriesIndex)
|
||||||
|
return Style{
|
||||||
|
StrokeColor: strokeColor,
|
||||||
|
StrokeWidth: DefaultStrokeWidth,
|
||||||
|
Font: pc.GetFont(),
|
||||||
|
FontSize: DefaultFontSize,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pc PieChart) styleDefaultsElements() Style {
|
||||||
|
return Style{
|
||||||
|
Font: pc.GetFont(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Box returns the chart bounds as a box.
|
||||||
|
func (pc PieChart) 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,
|
||||||
|
}
|
||||||
|
}
|
|
@ -71,6 +71,16 @@ func (rr *rasterRenderer) LineTo(x, y int) {
|
||||||
rr.gc.LineTo(float64(x), float64(y))
|
rr.gc.LineTo(float64(x), float64(y))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// QuadCurveTo implements the interface method.
|
||||||
|
func (rr *rasterRenderer) QuadCurveTo(cx, cy, x, y int) {
|
||||||
|
rr.gc.QuadCurveTo(float64(cx), float64(cy), float64(x), float64(y))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArcTo implements the interface method.
|
||||||
|
func (rr *rasterRenderer) ArcTo(cx, cy int, rx, ry, startAngle, delta float64) {
|
||||||
|
rr.gc.ArcTo(float64(cx), float64(cy), rx, ry, startAngle, delta)
|
||||||
|
}
|
||||||
|
|
||||||
// Close implements the interface method.
|
// Close implements the interface method.
|
||||||
func (rr *rasterRenderer) Close() {
|
func (rr *rasterRenderer) Close() {
|
||||||
rr.gc.Close()
|
rr.gc.Close()
|
||||||
|
|
|
@ -34,6 +34,14 @@ type Renderer interface {
|
||||||
// from the previous point.
|
// from the previous point.
|
||||||
LineTo(x, y int)
|
LineTo(x, y int)
|
||||||
|
|
||||||
|
// QuadCurveTo draws a quad curve.
|
||||||
|
// cx and cy represent the bezier "control points".
|
||||||
|
QuadCurveTo(cx, cy, x, y int)
|
||||||
|
|
||||||
|
// ArcTo draws an arc with a given center (cx,cy)
|
||||||
|
// a given set of radii (rx,ry), a startAngle and delta (in radians).
|
||||||
|
ArcTo(cx, cy int, rx, ry, startAngle, delta float64)
|
||||||
|
|
||||||
// Close finalizes a shape as drawn by LineTo.
|
// Close finalizes a shape as drawn by LineTo.
|
||||||
Close()
|
Close()
|
||||||
|
|
||||||
|
|
7
style.go
7
style.go
|
@ -79,11 +79,11 @@ func (s Style) String() string {
|
||||||
if s.FontSize != 0 {
|
if s.FontSize != 0 {
|
||||||
output = append(output, fmt.Sprintf("\"font_size\": \"%0.2fpt\"", s.FontSize))
|
output = append(output, fmt.Sprintf("\"font_size\": \"%0.2fpt\"", s.FontSize))
|
||||||
} else {
|
} else {
|
||||||
output = append(output, "\"fill_color\": null")
|
output = append(output, "\"font_size\": null")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !s.FillColor.IsZero() {
|
if !s.FontColor.IsZero() {
|
||||||
output = append(output, fmt.Sprintf("\"font_color\": %s", s.FillColor.String()))
|
output = append(output, fmt.Sprintf("\"font_color\": %s", s.FontColor.String()))
|
||||||
} else {
|
} else {
|
||||||
output = append(output, "\"font_color\": null")
|
output = append(output, "\"font_color\": null")
|
||||||
}
|
}
|
||||||
|
@ -190,6 +190,7 @@ func (s Style) PersistToRenderer(r Renderer) {
|
||||||
r.SetStrokeColor(s.GetStrokeColor())
|
r.SetStrokeColor(s.GetStrokeColor())
|
||||||
r.SetStrokeWidth(s.GetStrokeWidth())
|
r.SetStrokeWidth(s.GetStrokeWidth())
|
||||||
r.SetStrokeDashArray(s.GetStrokeDashArray())
|
r.SetStrokeDashArray(s.GetStrokeDashArray())
|
||||||
|
r.SetFillColor(s.GetFillColor())
|
||||||
r.SetFont(s.GetFont())
|
r.SetFont(s.GetFont())
|
||||||
r.SetFontColor(s.GetFontColor())
|
r.SetFontColor(s.GetFontColor())
|
||||||
r.SetFontSize(s.GetFontSize())
|
r.SetFontSize(s.GetFontSize())
|
||||||
|
|
43
util.go
43
util.go
|
@ -99,6 +99,21 @@ func RoundDown(value, roundTo float64) float64 {
|
||||||
return d1 * roundTo
|
return d1 * roundTo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Normalize returns a set of numbers on the interval [0,1] for a given set of inputs.
|
||||||
|
// An example: 4,3,2,1 => 0.4, 0.3, 0.2, 0.1
|
||||||
|
// Caveat; the total may be < 1.0; there are going to be issues with irrational numbers etc.
|
||||||
|
func Normalize(values ...float64) []float64 {
|
||||||
|
var total float64
|
||||||
|
for _, v := range values {
|
||||||
|
total += v
|
||||||
|
}
|
||||||
|
output := make([]float64, len(values))
|
||||||
|
for x, v := range values {
|
||||||
|
output[x] = RoundDown(v/total, 0.001)
|
||||||
|
}
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
// MinInt returns the minimum of a set of integers.
|
// MinInt returns the minimum of a set of integers.
|
||||||
func MinInt(values ...int) int {
|
func MinInt(values ...int) int {
|
||||||
min := math.MaxInt32
|
min := math.MaxInt32
|
||||||
|
@ -175,3 +190,31 @@ func SeqDays(days int) []time.Time {
|
||||||
func PercentDifference(v1, v2 float64) float64 {
|
func PercentDifference(v1, v2 float64) float64 {
|
||||||
return (v2 - v1) / v1
|
return (v2 - v1) / v1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DegreesToRadians returns degrees as radians.
|
||||||
|
func DegreesToRadians(degrees float64) float64 {
|
||||||
|
return degrees * (math.Pi / 180.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
_2pi = 2 * math.Pi
|
||||||
|
_3pi4 = (3 * math.Pi) / 4.0
|
||||||
|
_pi2 = math.Pi / 2.0
|
||||||
|
_pi4 = math.Pi / 4.0
|
||||||
|
)
|
||||||
|
|
||||||
|
// PercentToRadians converts a normalized value (0,1) to radians.
|
||||||
|
func PercentToRadians(pct float64) float64 {
|
||||||
|
return DegreesToRadians(360.0 * pct)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RadianAdd adds a delta to a base in radians.
|
||||||
|
func RadianAdd(base, delta float64) float64 {
|
||||||
|
value := base + delta
|
||||||
|
if value > _2pi {
|
||||||
|
return math.Mod(value, _2pi)
|
||||||
|
} else if value < 0 {
|
||||||
|
return _2pi + value
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
|
@ -76,6 +76,15 @@ func (vr *vectorRenderer) LineTo(x, y int) {
|
||||||
vr.p = append(vr.p, fmt.Sprintf("L %d %d", x, y))
|
vr.p = append(vr.p, fmt.Sprintf("L %d %d", x, y))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// QuadCurveTo draws a quad curve.
|
||||||
|
func (vr *vectorRenderer) QuadCurveTo(cx, cy, x, y int) {
|
||||||
|
vr.p = append(vr.p, fmt.Sprintf("Q%d,%d %d,%d", cx, cy, x, y))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vr *vectorRenderer) ArcTo(cx, cy int, rx, ry, startAngle, delta float64) {
|
||||||
|
vr.p = append(vr.p, fmt.Sprintf("A %d %d %0.2f 1 1 %d %d", int(rx), int(ry), delta, cx, cy))
|
||||||
|
}
|
||||||
|
|
||||||
// Close closes a shape.
|
// Close closes a shape.
|
||||||
func (vr *vectorRenderer) Close() {
|
func (vr *vectorRenderer) Close() {
|
||||||
vr.p = append(vr.p, fmt.Sprintf("Z"))
|
vr.p = append(vr.p, fmt.Sprintf("Z"))
|
||||||
|
|
Loading…
Reference in a new issue