it's a graph

master
Jordan Orelli 8 years ago
parent e6bac6ae0f
commit 457fc2af1c

@ -5,13 +5,6 @@ import (
"testing"
)
var (
white = color.RGBA{0xff, 0xff, 0xff, 0xff}
red = color.RGBA{0xff, 0, 0, 0xff}
green = color.RGBA{0, 0xff, 0, 0xff}
blue = color.RGBA{0, 0, 0xff, 0xff}
)
func TestParseColor(t *testing.T) {
white := color.RGBA{0xff, 0xff, 0xff, 0xff}

@ -0,0 +1,28 @@
package main
func coalesce(errs ...error) error {
for _, err := range errs {
if err != nil {
return err
}
}
return nil
}
// provided a number and a bounding range for that number, normalizes that
// number within that bounds (linearly). that is, it returns a float between 0
// and 1 representing i's position within the (min,max) range. Min and max are
// both inclusive.
func norm(i, min, max int) (n float64) {
if i < min {
return 0
}
if i > max {
return 1
}
span := max - min
reach := i - min
return float64(reach) / float64(span)
}

@ -0,0 +1,31 @@
package main
import (
"testing"
)
func TestNorm(t *testing.T) {
type test struct {
i int
min int
max int
n float64
}
tests := []test{
{1, 2, 3, 0},
{3, 2, 1, 1},
{1, 0, 10, 0.1},
{5, 0, 10, 0.5},
{125, 100, 200, 0.25},
{-125, -200, -100, 0.75},
}
for _, test := range tests {
n := norm(test.i, test.min, test.max)
if n == test.n {
t.Logf("norm(%d, %d, %d) == %f", test.i, test.min, test.max, n)
} else {
t.Errorf("norm(%d, %d, %d) is %f, expected %f", test.i, test.min, test.max, n, test.n)
}
}
}

@ -14,6 +14,9 @@ import (
var (
white = color.RGBA{0xff, 0xff, 0xff, 0xff}
red = color.RGBA{0xff, 0, 0, 0xff}
green = color.RGBA{0, 0xff, 0, 0xff}
blue = color.RGBA{0, 0, 0xff, 0xff}
)
type server struct {
@ -27,6 +30,7 @@ func (s *server) logReceived(r *http.Request) {
func (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
s.logReceived(r)
encoder, err := getEncoding(r)
if err != nil {
s.writeError(w, err)
@ -36,9 +40,29 @@ func (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
width := parseInt(r.URL.Query().Get("w"), 400)
height := parseInt(r.URL.Query().Get("h"), 400)
bg := parseColor(r.URL.Query().Get("bg"), white)
data := parseSeries(r.URL.Query().Get("s"), series{})
// create a new canvas
m := image.NewRGBA(image.Rect(0, 0, width, height))
// paint the background
draw.Draw(m, m.Bounds(), &image.Uniform{bg}, m.Bounds().Min, draw.Src)
// compute column dimensions
num_cols := data.len()
col_width := width
if num_cols > 0 {
col_width = width / num_cols // float here?
}
fg := &image.Uniform{blue}
for idx, val := range data {
col_height_n := norm(val, data.min(), data.max())
col_height := int(col_height_n * float64(height))
x := idx * col_width
rect := image.Rect(x, height, x+col_width, height-col_height)
draw.Draw(m, rect, fg, image.ZP, draw.Src)
}
encoder.WriteImage(w, m)
}
@ -91,6 +115,62 @@ func parseColor(c_s string, d color.RGBA) color.RGBA {
return c
}
type series []int
// minimum value in the series
func (s series) min() int {
switch len(s) {
case 0:
return 0
case 1:
return s[0]
default:
}
m := s[0]
for _, i := range s[1:] {
if i < m {
m = i
}
}
return m
}
// maximum value in the series
func (s series) max() int {
switch len(s) {
case 0:
return 0
case 1:
return s[0]
default:
}
m := s[0]
for _, i := range s[1:] {
if i > m {
m = i
}
}
return m
}
func (s series) len() int {
return len(s)
}
// parses a user-supplied series
func parseSeries(s string, missing series) series {
parts := strings.Split(s, ",")
out := make(series, 0, len(parts))
for _, p := range parts {
n, err := strconv.Atoi(p)
if err != nil {
return missing
}
out = append(out, n)
}
return out
}
type imageWriter interface {
WriteImage(w http.ResponseWriter, m image.Image)
}

Loading…
Cancel
Save