vector renderer works

This commit is contained in:
Will Charczuk 2016-07-28 13:22:18 -07:00
parent b600cb1994
commit 3d9cf0da0c
5 changed files with 129 additions and 16 deletions

View file

@ -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)
}

View file

@ -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
View file

@ -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
}

View file

@ -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))
}

View file

@ -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.