vector renderer works
This commit is contained in:
parent
b600cb1994
commit
3d9cf0da0c
5 changed files with 129 additions and 16 deletions
|
@ -14,17 +14,18 @@ func drawChart(res http.ResponseWriter, req *http.Request) {
|
|||
FillColor: chart.ColorLightGray,
|
||||
},
|
||||
Values: []chart.PieChartValue{
|
||||
{Value: 0.3, Label: "Blue"},
|
||||
{Value: 0.2, Label: "Blue"},
|
||||
{Value: 0.2, Label: "Green"},
|
||||
{Value: 0.2, Label: "Gray"},
|
||||
{Value: 0.1, Label: "Orange"},
|
||||
{Value: 0.1, Label: "HEANG"},
|
||||
{Value: 0.1, Label: "??"},
|
||||
{Value: 0.1, Label: "!!"},
|
||||
},
|
||||
}
|
||||
|
||||
res.Header().Set("Content-Type", "image/png")
|
||||
err := pie.Render(chart.PNG, res)
|
||||
res.Header().Set("Content-Type", "image/svg+xml")
|
||||
err := pie.Render(chart.SVG, res)
|
||||
if err != nil {
|
||||
fmt.Printf("Error rendering pie chart: %v\n", err)
|
||||
}
|
||||
|
|
13
pie_chart.go
13
pie_chart.go
|
@ -3,7 +3,6 @@ package chart
|
|||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"math"
|
||||
|
||||
"github.com/golang/freetype/truetype"
|
||||
)
|
||||
|
@ -142,14 +141,15 @@ 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
|
||||
labelRadius := (radius * 2.0) / 3.0
|
||||
|
||||
// draw the pie slices
|
||||
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)
|
||||
|
||||
r.MoveTo(cx, cy)
|
||||
rads = PercentToRadians(total)
|
||||
delta = PercentToRadians(v.Value)
|
||||
|
||||
|
@ -161,13 +161,14 @@ func (pc PieChart) drawSlices(r Renderer, canvasBox Box, values []PieChartValue)
|
|||
total = total + v.Value
|
||||
}
|
||||
|
||||
// draw the labels
|
||||
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))
|
||||
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)
|
||||
|
|
48
util.go
48
util.go
|
@ -191,18 +191,30 @@ func PercentDifference(v1, v2 float64) float64 {
|
|||
return (v2 - v1) / v1
|
||||
}
|
||||
|
||||
// DegreesToRadians returns degrees as radians.
|
||||
func DegreesToRadians(degrees float64) float64 {
|
||||
return degrees * (math.Pi / 180.0)
|
||||
}
|
||||
|
||||
const (
|
||||
_pi = math.Pi
|
||||
_2pi = 2 * math.Pi
|
||||
_3pi4 = (3 * math.Pi) / 4.0
|
||||
_4pi3 = (4 * math.Pi) / 3.0
|
||||
_3pi2 = (3 * math.Pi) / 2.0
|
||||
_5pi4 = (5 * math.Pi) / 4.0
|
||||
_7pi4 = (7 * math.Pi) / 4.0
|
||||
_pi2 = math.Pi / 2.0
|
||||
_pi4 = math.Pi / 4.0
|
||||
_d2r = (math.Pi / 180.0)
|
||||
_r2d = (180.0 / math.Pi)
|
||||
)
|
||||
|
||||
// DegreesToRadians returns degrees as radians.
|
||||
func DegreesToRadians(degrees float64) float64 {
|
||||
return degrees * _d2r
|
||||
}
|
||||
|
||||
// RadiansToDegrees translates a radian value to a degree value.
|
||||
func RadiansToDegrees(value float64) float64 {
|
||||
return math.Mod(value, _2pi) * _r2d
|
||||
}
|
||||
|
||||
// PercentToRadians converts a normalized value (0,1) to radians.
|
||||
func PercentToRadians(pct float64) float64 {
|
||||
return DegreesToRadians(360.0 * pct)
|
||||
|
@ -214,7 +226,31 @@ func RadianAdd(base, delta float64) float64 {
|
|||
if value > _2pi {
|
||||
return math.Mod(value, _2pi)
|
||||
} else if value < 0 {
|
||||
return _2pi + value
|
||||
return math.Mod(_2pi+value, _2pi)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// DegreesAdd adds a delta to a base in radians.
|
||||
func DegreesAdd(baseDegrees, deltaDegrees float64) float64 {
|
||||
value := baseDegrees + deltaDegrees
|
||||
if value > _2pi {
|
||||
return math.Mod(value, 360.0)
|
||||
} else if value < 0 {
|
||||
return math.Mod(360.0+value, 360.0)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// DegreesToCompass returns the degree value in compass / clock orientation.
|
||||
func DegreesToCompass(deg float64) float64 {
|
||||
return DegreesAdd(deg, -90.0)
|
||||
}
|
||||
|
||||
// CirclePoint returns the absolute position of a circle diameter point given
|
||||
// by the radius and the angle.
|
||||
func CirclePoint(cx, cy int, radius, angleRadians float64) (x, y int) {
|
||||
x = cx + int(radius*math.Sin(angleRadians))
|
||||
y = cy - int(radius*math.Cos(angleRadians))
|
||||
return
|
||||
}
|
||||
|
|
57
util_test.go
57
util_test.go
|
@ -100,3 +100,60 @@ func TestPercentDifference(t *testing.T) {
|
|||
assert.Equal(0.5, PercentDifference(1.0, 1.5))
|
||||
assert.Equal(-0.5, PercentDifference(2.0, 1.0))
|
||||
}
|
||||
|
||||
var (
|
||||
_degreesToRadians = map[float64]float64{
|
||||
0: 0, // !_2pi b/c no irrational nums in floats.
|
||||
45: _pi4,
|
||||
90: _pi2,
|
||||
135: _3pi4,
|
||||
180: _pi,
|
||||
225: _5pi4,
|
||||
270: _3pi2,
|
||||
315: _7pi4,
|
||||
}
|
||||
|
||||
_compassToRadians = map[float64]float64{
|
||||
0: _pi2,
|
||||
45: _pi4,
|
||||
90: 0, // !_2pi b/c no irrational nums in floats.
|
||||
135: _7pi4,
|
||||
180: _3pi2,
|
||||
225: _5pi4,
|
||||
270: _pi,
|
||||
315: _3pi4,
|
||||
}
|
||||
)
|
||||
|
||||
func TestDegreesToRadians(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
for d, r := range _degreesToRadians {
|
||||
assert.Equal(r, DegreesToRadians(d))
|
||||
}
|
||||
}
|
||||
|
||||
func TestPercentToRadians(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
for d, r := range _degreesToRadians {
|
||||
assert.Equal(r, PercentToRadians(d/360.0))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRadiansToDegrees(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
for d, r := range _degreesToRadians {
|
||||
assert.Equal(d, RadiansToDegrees(r))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRadianAdd(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
assert.Equal(_pi, RadianAdd(_pi2, _pi2))
|
||||
assert.Equal(_3pi2, RadianAdd(_pi2, _pi))
|
||||
assert.Equal(_pi, RadianAdd(_pi, _2pi))
|
||||
assert.Equal(_pi, RadianAdd(_pi, -_2pi))
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/image/font"
|
||||
|
@ -82,7 +83,24 @@ func (vr *vectorRenderer) QuadCurveTo(cx, cy, x, y int) {
|
|||
}
|
||||
|
||||
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))
|
||||
startAngle = RadianAdd(startAngle, _pi2)
|
||||
endAngle := RadianAdd(startAngle, delta)
|
||||
|
||||
startx := cx + int(rx*math.Sin(startAngle))
|
||||
starty := cy - int(ry*math.Cos(startAngle))
|
||||
|
||||
if len(vr.p) > 0 {
|
||||
vr.p = append(vr.p, fmt.Sprintf("L %d %d", startx, starty))
|
||||
} else {
|
||||
vr.p = append(vr.p, fmt.Sprintf("M %d %d", startx, starty))
|
||||
}
|
||||
|
||||
endx := cx + int(rx*math.Sin(endAngle))
|
||||
endy := cy - int(ry*math.Cos(endAngle))
|
||||
|
||||
dd := RadiansToDegrees(delta)
|
||||
|
||||
vr.p = append(vr.p, fmt.Sprintf("A %d %d %0.2f 0 1 %d %d", int(rx), int(ry), dd, endx, endy))
|
||||
}
|
||||
|
||||
// Close closes a shape.
|
||||
|
|
Loading…
Reference in a new issue