Example #1
0
// Approximate a circular arc using multiple
// cubic Bézier curves, one for each π/2 segment.
//
// This is from:
// 	http://hansmuller-flex.blogspot.com/2011/04/approximating-circular-arc-with-cubic.html
func arc(p *pdf.Path, comp vg.PathComp) {
	x0 := comp.X + comp.Radius*vg.Length(math.Cos(comp.Start))
	y0 := comp.Y + comp.Radius*vg.Length(math.Sin(comp.Start))
	p.Line(pdfPoint(x0, y0))

	a1 := comp.Start
	end := a1 + comp.Angle
	sign := 1.0
	if end < a1 {
		sign = -1.0
	}
	left := math.Abs(comp.Angle)

	// Square root of the machine epsilon for IEEE 64-bit floating
	// point values.  This is the equality threshold recommended
	// in Numerical Recipes, if I recall correctly—it's small enough.
	const epsilon = 1.4901161193847656e-08

	for left > epsilon {
		a2 := a1 + sign*math.Min(math.Pi/2, left)
		partialArc(p, comp.X, comp.Y, comp.Radius, a1, a2)
		left -= math.Abs(a2 - a1)
		a1 = a2
	}
}
Example #2
0
// crosshair draws a plus at the given point.
func crosshair(img draw.Image, x, y int, str string) {
	c := vgimg.NewImage(img)

	// drawPlots here because NewImage
	// clears the canvas.  Instead, the canvas
	// should just be stored instead of being
	// recreated at each redraw.
	drawPlots(img)

	c.SetColor(color.RGBA{R: 255, A: 255})

	xc := vg.Inches(float64(x) / c.DPI())
	yc := vg.Inches(float64(y) / c.DPI())
	radius := vg.Points(5)

	var p vg.Path
	p.Move(xc-radius, yc)
	p.Line(xc+radius, yc)
	c.Stroke(p)

	p = vg.Path{}
	p.Move(xc, yc+radius)
	p.Line(xc, yc-radius)
	c.Stroke(p)

	c.SetColor(color.Black)
	c.FillString(font, vg.Length(0), vg.Length(0), str)
}
Example #3
0
// Height returns the height of the text when using
// the given font.
func (sty TextStyle) Height(txt string) vg.Length {
	nl := textNLines(txt)
	if nl == 0 {
		return vg.Length(0)
	}
	e := sty.Font.Extents()
	return e.Height*vg.Length(nl-1) + e.Ascent
}
Example #4
0
// GlyphBoxes returns a slice of GlyphBoxes,
// one for each of the labels, implementing the
// plot.GlyphBoxer interface.
func (l *Labels) GlyphBoxes(p *plot.Plot) []plot.GlyphBox {
	bs := make([]plot.GlyphBox, len(l.Labels))
	for i, label := range l.Labels {
		bs[i].X = p.X.Norm(l.XYs[i].X)
		bs[i].Y = p.Y.Norm(l.XYs[i].Y)
		w := l.Width(label)
		h := l.Height(label)
		bs[i].Rect.Min.X = w*vg.Length(l.XAlign) + l.XOffset
		bs[i].Rect.Min.Y = h*vg.Length(l.YAlign) + l.YOffset
		bs[i].Rect.Size.X = w
		bs[i].Rect.Size.Y = h
	}
	return bs
}
Example #5
0
func (s *SparkLines) Plot(da plot.DrawArea, plt *plot.Plot) {
	trX, trY := plt.Transforms(&da)

	w := vg.Length(1)

	da.SetLineWidth(w)

	_, _, ymin, ymax := s.DataRange()

	for _, d := range s.XYs {
		perc := float64(d.Y-ymin) / float64(ymax-ymin)
		c := BrightColorGradient.GetInterpolatedColorFor((perc*-1+1)*0.5 + 0.6)
		da.SetColor(c)

		// Transform the data x, y coordinate of this bubble
		// to the corresponding drawing coordinate.
		x := trX(d.X)
		y := trY(d.Y * 0.9)

		//rad := vg.Length(10)
		var p vg.Path
		p.Move(x-w, y)
		p.Line(x-w, 0)
		//p.Close()
		da.Stroke(p)

		//da.StrokeLine2(*sty, x, 0, x, y)
	}
}
Example #6
0
func (pt *Dots) Plot(da plot.DrawArea, plt *plot.Plot) {
	trX, trY := plt.Transforms(&da)

	da.SetColor(pt.Color)

	for i := range pt.Y {
		// Transform the data x, y coordinate of this bubble
		// to the corresponding drawing coordinate.
		x := trX(pt.X[i])
		y := trY(pt.Y[i])

		// Get the radius of this bubble.  The radius
		// is specified in drawing units (i.e., its size
		// is given as the final size at which it will
		// be drawn) so it does not need to be transformed.
		rad := vg.Length(2)

		// Fill a circle centered at x,y on the draw area.
		var p vg.Path
		p.Move(x+rad, y)
		p.Arc(x, y, rad, 0, 2*math.Pi)
		p.Close()
		da.Fill(p)
	}
}
Example #7
0
// draw draws the legend to the given DrawArea.
func (l *Legend) draw(da DrawArea) {
	iconx := da.Min.X
	textx := iconx + l.ThumbnailWidth + l.TextStyle.Width(" ")
	xalign := 0.0
	if !l.Left {
		iconx = da.Max().X - l.ThumbnailWidth
		textx = iconx - l.TextStyle.Width(" ")
		xalign = -1
	}
	textx += l.XOffs
	iconx += l.XOffs

	enth := l.entryHeight()
	y := da.Max().Y - enth
	if !l.Top {
		y = da.Min.Y + (enth+l.Padding)*(vg.Length(len(l.entries))-1)
	}
	y += l.YOffs

	icon := &DrawArea{
		Canvas: da.Canvas,
		Rect:   Rect{Min: Point{iconx, y}, Size: Point{l.ThumbnailWidth, enth}},
	}
	for _, e := range l.entries {
		for _, t := range e.thumbs {
			t.Thumbnail(icon)
		}
		yoffs := (enth - l.TextStyle.Height(e.text)) / 2
		da.FillText(l.TextStyle, textx, icon.Min.Y+yoffs, xalign, 0, e.text)
		icon.Min.Y -= enth + l.Padding
	}
}
Example #8
0
// radius returns the radius of a bubble by linear interpolation.
func (bs *Bubbles) radius(z float64) vg.Length {
	rng := bs.MaxRadius - bs.MinRadius
	if bs.MaxZ == bs.MinZ {
		return rng/2 + bs.MinRadius
	}
	d := (z - bs.MinZ) / (bs.MaxZ - bs.MinZ)
	return vg.Length(d)*rng + bs.MinRadius
}
Example #9
0
// FillText fills lines of text in the draw area.
// The text is offset by its width times xalign and
// its height times yalign.  x and y give the bottom
// left corner of the text befor e it is offset.
func (da *DrawArea) FillText(sty TextStyle, x, y vg.Length, xalign, yalign float64, txt string) {
	txt = strings.TrimRight(txt, "\n")
	if len(txt) == 0 {
		return
	}

	da.SetColor(sty.Color)

	ht := sty.Height(txt)
	y += ht*vg.Length(yalign) - sty.Font.Extents().Ascent
	nl := textNLines(txt)
	for i, line := range strings.Split(txt, "\n") {
		xoffs := vg.Length(xalign) * sty.Font.Width(line)
		n := vg.Length(nl - i)
		da.FillString(sty.Font, x+xoffs, y+n*sty.Font.Size, line)
	}
}
Example #10
0
// Approximate a circular arc of fewer than π/2
// radians with cubic Bézier curve.
func partialArc(p *pdf.Path, x, y, r vg.Length, a1, a2 float64) {
	a := (a2 - a1) / 2
	x4 := r * vg.Length(math.Cos(a))
	y4 := r * vg.Length(math.Sin(a))
	x1 := x4
	y1 := -y4

	const k = 0.5522847498 // some magic constant
	f := k * vg.Length(math.Tan(a))
	x2 := x1 + f*y4
	y2 := y1 + f*x4
	x3 := x2
	y3 := -y2

	// Rotate and translate points into position.
	ar := a + a1
	sinar := vg.Length(math.Sin(ar))
	cosar := vg.Length(math.Cos(ar))
	x2r := x2*cosar - y2*sinar + x
	y2r := x2*sinar + y2*cosar + y
	x3r := x3*cosar - y3*sinar + x
	y3r := x3*sinar + y3*cosar + y
	x4 = r*vg.Length(math.Cos(a2)) + x
	y4 = r*vg.Length(math.Sin(a2)) + y
	p.Curve(pdfPoint(x2r, y2r), pdfPoint(x3r, y3r), pdfPoint(x4, y4))
}
Example #11
0
// Approximate a circular arc using multiple
// cubic Bézier curves, one for each π/2 segment.
//
// This is from:
// 	http://hansmuller-flex.blogspot.com/2011/04/approximating-circular-arc-with-cubic.html
func arc(p *pdf.Path, comp vg.PathComp) {
	x0 := comp.X + comp.Radius*vg.Length(math.Cos(comp.Start))
	y0 := comp.Y + comp.Radius*vg.Length(math.Sin(comp.Start))
	p.Line(pdfPoint(x0, y0))

	a1 := comp.Start
	end := a1 + comp.Angle
	sign := 1.0
	if end < a1 {
		sign = -1.0
	}
	left := math.Abs(comp.Angle)
	for left > 0 {
		a2 := a1 + sign*math.Min(math.Pi/2, left)
		partialArc(p, comp.X, comp.Y, comp.Radius, a1, a2)
		left -= math.Abs(a2 - a1)
		a1 = a2
	}
}
Example #12
0
// padX returns a DrawArea that is padded horizontally
// so that glyphs will no be clipped.
func padX(p *Plot, da DrawArea) DrawArea {
	glyphs := p.GlyphBoxes(p)
	l := leftMost(&da, glyphs)
	xAxis := horizontalAxis{p.X}
	glyphs = append(glyphs, xAxis.GlyphBoxes(p)...)
	r := rightMost(&da, glyphs)

	minx := da.Min.X - l.Min.X
	maxx := da.Max().X - (r.Min.X + r.Size.X)
	lx := vg.Length(l.X)
	rx := vg.Length(r.X)
	n := (lx*maxx - rx*minx) / (lx - rx)
	m := ((lx-1)*maxx - rx*minx + minx) / (lx - rx)
	return DrawArea{
		Canvas: vg.Canvas(da),
		Rect: Rect{
			Min:  Point{X: n, Y: da.Min.Y},
			Size: Point{X: m - n, Y: da.Size.Y},
		},
	}
}
Example #13
0
// padY returns a DrawArea that is padded vertically
// so that glyphs will no be clipped.
func padY(p *Plot, da DrawArea) DrawArea {
	glyphs := p.GlyphBoxes(p)
	b := bottomMost(&da, glyphs)
	yAxis := verticalAxis{p.Y}
	glyphs = append(glyphs, yAxis.GlyphBoxes(p)...)
	t := topMost(&da, glyphs)

	miny := da.Min.Y - b.Min.Y
	maxy := da.Max().Y - (t.Min.Y + t.Size.Y)
	by := vg.Length(b.Y)
	ty := vg.Length(t.Y)
	n := (by*maxy - ty*miny) / (by - ty)
	m := ((by-1)*maxy - ty*miny + miny) / (by - ty)
	return DrawArea{
		Canvas: vg.Canvas(da),
		Rect: Rect{
			Min:  Point{Y: n, X: da.Min.X},
			Size: Point{Y: m - n, X: da.Size.X},
		},
	}
}
Example #14
0
// tickLabelHeight returns height of the tick mark labels.
func tickLabelHeight(sty TextStyle, ticks []Tick) vg.Length {
	maxHeight := vg.Length(0)
	for _, t := range ticks {
		if t.IsMinor() {
			continue
		}
		h := sty.Height(t.Label)
		if h > maxHeight {
			maxHeight = h
		}
	}
	return maxHeight
}
// Report builds up a plot of the response times of the requests
// in SVG format and writes it to out
func (r *TimingsPlotReporter) Report(out io.Writer) error {
	timestamps := make([]time.Time, 0)
	timings := make([]time.Duration, 0)

	for e := r.responses.Front(); e != nil; e = e.Next() {
		r := e.Value.(*result)
		timestamps = append(timestamps, r.timestamp)
		timings = append(timings, r.timing)
	}

	p, err := plot.New()
	if err != nil {
		return err
	}
	pts := make(plotter.XYs, len(timestamps))
	for i := 0; i < len(pts); i++ {
		pts[i].X = timestamps[i].Sub(timestamps[0]).Seconds()
		pts[i].Y = timings[i].Seconds() * 1000
	}

	line, err := plotter.NewLine(pts)
	if err != nil {
		return err
	}
	line.Color = plotutil.Color(1)

	p.Add(line)
	p.X.Padding = vg.Length(3.0)
	p.X.Label.Text = "Time elapsed"
	p.Y.Padding = vg.Length(3.0)
	p.Y.Label.Text = "Latency (ms)"

	w, h := vg.Millimeters(float64(len(timestamps))), vg.Centimeters(12.0)
	canvas := vgsvg.New(w, h)
	p.Draw(plot.MakeDrawArea(canvas))

	_, err = canvas.WriteTo(out)
	return err
}
Example #16
0
// tickLabelWidth returns the width of the widest tick mark label.
func tickLabelWidth(sty TextStyle, ticks []Tick) vg.Length {
	maxWidth := vg.Length(0)
	for _, t := range ticks {
		if t.IsMinor() {
			continue
		}
		w := sty.Width(t.Label)
		if w > maxWidth {
			maxWidth = w
		}
	}
	return maxWidth
}
Example #17
0
func RunServer(config *Config) {
	// manually load fonts from resource
	for _, fontasset := range AssetNames() {
		if strings.HasPrefix(fontasset, "res/fonts/") {
			fontname := strings.TrimSuffix(path.Base(fontasset), path.Ext(fontasset))
			fontbytes, err := Asset(fontasset)
			if err != nil {
				panic(err)
			}
			fontreader := bytes.NewReader(fontbytes)
			vg.LoadFont(fontname, fontreader)
		}
	}

	// create memory caches
	homeCache := cache.New(10*time.Minute, 5*time.Minute)
	categoryCache := cache.New(30*time.Minute, 30*time.Minute)
	detailCache := cache.New(15*time.Minute, 10*time.Minute)

	initialCheck := func(w http.ResponseWriter, r *http.Request) bool {
		// clear cache if requested
		nocache := r.Form.Get("nocache")
		if nocache == "1" {
			homeCache.Flush()
			categoryCache.Flush()
			detailCache.Flush()
		}

		return true
	}

	http.HandleFunc("/favicon.ico", func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, "Not found", 404)
	})
	http.HandleFunc("/favicon.png", func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, "Not found", 404)
	})

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		//config.Logger.Printf("Connection: %s\n", r.URL.String())

		var err error

		r.ParseForm()

		if !initialCheck(w, r) {
			return
		}

		csession := config.Session.Clone()
		defer csession.Close()

		cat := r.Form.Get("category")
		chart := r.Form.Get("chart")
		p_page := r.Form.Get("pg")
		page := 1
		if p_page != "" {
			t_page, err := strconv.ParseInt(p_page, 10, 32)
			if err != nil {
				http.Error(w, err.Error(), 500)
				return
			}
			page = int(t_page)
		}
		if page < 1 {
			page = 1
		}

		i := fstopinfo.NewInfo(config.Logger, csession)
		i.Database = config.Database

		// load categories
		var c fstopinfo.FSCategoryList

		catcache, catfound := categoryCache.Get("category")
		if catfound {
			c = catcache.(fstopinfo.FSCategoryList)
		}

		if c == nil {
			c, err = i.Categories()
			if err != nil {
				http.Error(w, err.Error(), 500)
				return
			}
			if c != nil {
				categoryCache.Set("category", c, 0)
			}
		}

		if c == nil {
			http.Error(w, "Could not load categories", 500)
			return
		}

		// load home
		var d fstopimp.FSTopStatsList

		var dcache interface{}
		var dfound bool
		var dname string
		if cat == "" {
			dname = "index"
		} else {
			// check if category exists
			if !c.Exists(cat) {
				http.Error(w, "Category not found", 404)
				return
			}
			dname = cat
		}
		dcache, dfound = homeCache.Get(dname)
		if dfound {
			d = dcache.(fstopimp.FSTopStatsList)
		}

		// data not on cache, load
		if d == nil {
			if cat == "" {
				d, err = i.Top(config.TopId)
			} else {
				d, err = i.TopCategory(config.TopId, cat)
			}
			if err != nil {
				http.Error(w, err.Error(), 500)
				return
			}
			if len(d) > 0 {
				homeCache.Set(dname, d, 0)
			}
		}

		pagecount := d.PageCount(config.PageSize)
		d = d.Paged(page, config.PageSize)

		w.Header().Add("Content-Type", "text/html; charset=utf-8")

		var body *bytes.Buffer = new(bytes.Buffer)

		tmpldata := map[string]interface{}{
			"Title":      config.Title,
			"page":       page,
			"categories": c,
		}

		fmt.Fprintln(body, "<table class=\"main\">")

		fmt.Fprintln(body, "<tr><th>Chart</th><th>Title</th><th width=\"8%\">Added</th><th width=\"5%\">Score</th><th width=\"5%\">Count</th><th width=\"5%\">Comm.</th></tr>")

		for _, ii := range d {
			fmt.Fprintf(body, "<tr>\n")

			if chart == "" {
				fmt.Fprintf(body, "<td><img style=\"height: 107px;\" src=\"/chart?id=%s&size=short\"></td>",
					ii.Id)
			}

			fmt.Fprintf(body, "<td><a href=\"%s\">%s</a> <a href=\"/view?id=%s\">[data]</a> <a href=\"/chart?id=%s\">[chart]</a></td>"+
				"<td align=\"center\" style=\"%s\">%s</td>"+
				"<td align=\"right\">%d</td><td align=\"center\">%d</td><td align=\"center\">%d</td>\n",
				ii.Link, ii.Title, ii.Id, ii.Id,
				StyleAddDate(ii.Last.AddDate), FormatAddDate(ii.Last.AddDate), ii.Score, ii.Count, ii.Last.Comments)

			fmt.Fprintf(body, "</tr>\n")

			if chart == "1" {
				fmt.Fprintf(body, "<tr><td colspan=\"5\" align=\"center\"><img src=\"/chart?id=%s&size=small\"/></td></tr>\n", ii.Id)
			}
		}
		fmt.Fprintln(body, "</table>")

		pageparams := url.Values{}
		if cat != "" {
			pageparams.Add("category", cat)
		}
		if page > 1 {
			pageparams.Set("pg", "1")
			tmpldata["page_first"] = fmt.Sprintf("/?%s", pageparams.Encode())
		}
		if page > 1 {
			pageparams.Set("pg", strconv.Itoa(page-1))
			tmpldata["page_prev"] = fmt.Sprintf("/?%s", pageparams.Encode())
		}
		if len(d) > 0 && page < pagecount {
			pageparams.Set("pg", strconv.Itoa(page+1))
			tmpldata["page_next"] = fmt.Sprintf("/?%s", pageparams.Encode())
		}
		if page != pagecount {
			pageparams.Set("pg", strconv.Itoa(pagecount))
			tmpldata["page_last"] = fmt.Sprintf("/?%s", pageparams.Encode())
		}

		tmpl := LoadTemplates("index")
		tmpldata["Body"] = template.HTML(body.String())

		err = tmpl.ExecuteTemplate(w, "index", tmpldata)
		if err != nil {
			http.Error(w, "Error processing template", 500)
			return
		}

	})

	http.HandleFunc("/view", func(w http.ResponseWriter, r *http.Request) {
		r.ParseForm()

		if !initialCheck(w, r) {
			return
		}

		csession := config.Session.Clone()
		defer csession.Close()

		w.Header().Add("Content-Type", "text/html; charset=utf-8")

		id := r.Form.Get("id")
		if id == "" {
			http.Error(w, "ID not sent", 500)
			return
		}

		i := fstopinfo.NewInfo(config.Logger, csession)
		i.Database = config.Database

		var d []*fstopinfo.FSInfoHistory
		var err error

		dcache, found := detailCache.Get(id)
		if found {
			d = dcache.([]*fstopinfo.FSInfoHistory)
		}

		if d == nil {
			d, err = i.History(id, config.HistoryDays)
			if err != nil {
				http.Error(w, err.Error(), 500)
				return
			}
			if d != nil {
				detailCache.Set(id, d, 0)
			}
		}

		if d == nil {
			http.Error(w, "Not found", 500)
			return
		}

		var body *bytes.Buffer = new(bytes.Buffer)

		fmt.Fprintln(body, "<table class=\"main\" border=\"1\">")

		fmt.Fprintln(body, "<tr><th>Date</th><th>Hour</th><th>Seeders</th><th>Leechers</th><th>Complete</th><th>Comments</th></tr>")

		var last *fstopinfo.FSInfoHistory
		first := true

		for _, ii := range d {
			//if ii.Item != nil {
			if ii.Item != nil && first {
				fmt.Fprintf(body, "<tr><td colspan=\"7\">%s <a href=\"%s\">[goto]</a></td></tr>", strings.TrimSpace(ii.Item.Title), ii.Item.Link)
				first = false
			}

			if last != nil {
				if ii.Item != nil && last.Item != nil {
					//fmt.Printf("%s - [%d] [%d] [%d]\n", item.Title, pi.Seeders-item.Last.Seeders,
					//pi.Leechers-item.Last.Leechers, pi.Complete-item.Last.Complete)

					seeders := int64(ii.Item.Seeders - last.Item.Seeders)
					leechers := int64(ii.Item.Leechers - last.Item.Leechers)
					complete := int64(ii.Item.Complete - last.Item.Complete)
					comments := int64(ii.Item.Comments - last.Item.Comments)

					fmt.Fprintf(body, "<tr><td>%s</td><td>%d</td><td>%d (%d)</td><td>%d (%d)</td><td>%d (%d)</td><td>%d (%d)</td></tr>",
						ii.Date, ii.Hour,
						ii.Item.Seeders, seeders,
						ii.Item.Leechers, leechers,
						ii.Item.Complete, complete,
						ii.Item.Comments, comments)
				} else if ii.Item != nil && last.Item == nil {
					fmt.Fprintf(body, "<tr><td>%s</td><td>%d</td><td>%d (-)</td><td>%d (-)</td><td>%d (-)</td><td>%d (-)</td></tr>",
						ii.Date, ii.Hour,
						ii.Item.Seeders,
						ii.Item.Leechers,
						ii.Item.Complete,
						ii.Item.Comments)
				} else {
					fmt.Fprintf(body, "<tr><td>%s</td><td>%d</td></tr>",
						ii.Date, ii.Hour)
				}
			}

			last = ii
			//}
		}

		fmt.Fprintln(body, "</table>")

		tmpl := LoadTemplates("base")
		tmpldata := map[string]interface{}{
			"Title": config.Title,
			"Body":  template.HTML(body.String()),
		}
		err = tmpl.ExecuteTemplate(w, "base", tmpldata)
		if err != nil {
			http.Error(w, "Error processing template", 500)
			return
		}
	})

	http.HandleFunc("/chart", func(w http.ResponseWriter, r *http.Request) {
		r.ParseForm()

		if !initialCheck(w, r) {
			return
		}

		csession := config.Session.Clone()
		defer csession.Close()

		id := r.Form.Get("id")
		if id == "" {
			w.Header().Add("Content-Type", "text/html; charset=utf-8")
			http.Error(w, "ID not sent", 500)
			return
		}
		size := r.Form.Get("size")

		i := fstopinfo.NewInfo(config.Logger, csession)
		i.Database = config.Database

		var d []*fstopinfo.FSInfoHistory
		var err error

		dcache, found := detailCache.Get(id)
		if found {
			d = dcache.([]*fstopinfo.FSInfoHistory)
		}

		if d == nil {
			d, err = i.History(id, config.HistoryDays)
			if err != nil {
				w.Header().Add("Content-Type", "text/html; charset=utf-8")
				http.Error(w, err.Error(), 500)
				return
			}
			if d != nil {
				detailCache.Set(id, d, 0)
			}
		}

		if d == nil {
			w.Header().Add("Content-Type", "text/html; charset=utf-8")
			http.Error(w, "Not found", 500)
			return
		}

		p, err := plot.New()
		if err != nil {
			panic(err)
		}

		p.Title.Text = "Chart"
		p.X.Label.Text = "Time"
		p.Y.Label.Text = "Amount"

		c_seeders := make(plotter.XYs, 0)
		c_leechers := make(plotter.XYs, 0)
		c_complete := make(plotter.XYs, 0)
		c_comments := make(plotter.XYs, 0)

		var last *fstopinfo.FSInfoHistory
		first := true
		cttotal := int32(0)

		for _, ii := range d {
			if ii.Item != nil {
				cttotal++

				if first {
					p.Title.Text = strings.TrimSpace(ii.Item.Title)
					first = false
				}

				if last != nil && last.Item != nil {
					c_seeders = append(c_seeders, struct{ X, Y float64 }{float64(cttotal), float64(ii.Item.Seeders)})
					c_leechers = append(c_leechers, struct{ X, Y float64 }{float64(cttotal), float64(ii.Item.Leechers)})
					c_complete = append(c_complete, struct{ X, Y float64 }{float64(cttotal), float64(ii.Item.Complete - last.Item.Complete)})
					c_comments = append(c_comments, struct{ X, Y float64 }{float64(cttotal), float64(ii.Item.Comments)})
				}

				last = ii
			}
		}

		w.Header().Add("Content-Type", "image/png")

		pl_seeders, err := plotter.NewLine(c_seeders)
		if err != nil {
			http.Error(w, err.Error(), 500)
			return
		}
		pl_seeders.LineStyle.Width = vg.Length(1)
		pl_seeders.LineStyle.Color = color.RGBA{R: 255, A: 255}
		p.Add(pl_seeders)
		p.Legend.Add("Seeders", pl_seeders)

		pl_leechers, err := plotter.NewLine(c_leechers)
		if err != nil {
			http.Error(w, err.Error(), 500)
			return
		}
		pl_leechers.LineStyle.Width = vg.Length(1)
		pl_leechers.LineStyle.Color = color.RGBA{G: 255, A: 255}
		p.Add(pl_leechers)
		p.Legend.Add("Leechers", pl_leechers)

		pl_complete, err := plotter.NewLine(c_complete)
		if err != nil {
			http.Error(w, err.Error(), 500)
			return
		}
		pl_complete.LineStyle.Width = vg.Length(1)
		pl_complete.LineStyle.Color = color.RGBA{B: 255, A: 255}
		p.Add(pl_complete)
		p.Legend.Add("@Complete", pl_complete)

		pl_comments, err := plotter.NewLine(c_comments)
		if err != nil {
			http.Error(w, err.Error(), 500)
			return
		}
		pl_comments.LineStyle.Width = vg.Length(1)
		pl_comments.LineStyle.Color = color.RGBA{R: 255, B: 255, A: 255}
		p.Add(pl_comments)
		p.Legend.Add("Comments", pl_comments)

		width := vg.Length(640)
		height := vg.Length(480)
		if size == "small" {
			width = vg.Length(640)
			height = vg.Length(160)
		} else if size == "short" {
			width = vg.Length(200)
			height = vg.Length(80)
			p.Title.Text = ""
			p.X.Label.Text = ""
			p.Y.Label.Text = ""
		}

		c := vgimg.PngCanvas{vgimg.New(width, height)}
		p.Draw(plot.MakeDrawArea(c))
		c.WriteTo(w)
	})

	http.HandleFunc("/res/", func(w http.ResponseWriter, r *http.Request) {
		p := strings.Replace(path.Clean(r.URL.Path), "/res/", "res/files/", 1)

		d, err := Asset(p)
		if err != nil {
			http.Error(w, "Error loading resource", 500)
			return
		}

		w.Header().Add("Content-Type", mime.TypeByExtension(path.Ext(p)))
		w.Write(d)

		//fmt.Fprintf(w, "RES %s\n", p)
	})

	http.ListenAndServe(fmt.Sprintf("localhost:%d", config.Port), nil)

}
Example #18
0
// Y returns the value of x, given in the unit range,
// in the drawing coordinates of this draw area.
// A value of 0, for example, will return the minimum
// y value of the draw area and a value of 1 will
// return the maximum.
func (da *DrawArea) Y(y float64) vg.Length {
	return vg.Length(y)*(da.Max().Y-da.Min.Y) + da.Min.Y
}
Example #19
0
// X returns the value of x, given in the unit range,
// in the drawing coordinates of this draw area.
// A value of 0, for example, will return the minimum
// x value of the draw area and a value of 1 will
// return the maximum.
func (da *DrawArea) X(x float64) vg.Length {
	return vg.Length(x)*(da.Max().X-da.Min.X) + da.Min.X
}
Example #20
0
// RingGlyph is a glyph that draws the outline of a circle.
type RingGlyph struct{}

// DrawGlyph implements the Glyph interface.
func (RingGlyph) DrawGlyph(da *DrawArea, sty GlyphStyle, pt Point) {
	da.SetLineStyle(LineStyle{Color: sty.Color, Width: vg.Points(0.5)})
	var p vg.Path
	p.Move(pt.X+sty.Radius, pt.Y)
	p.Arc(pt.X, pt.Y, sty.Radius, 0, 2*math.Pi)
	p.Close()
	da.Stroke(p)
}

const (
	cosπover4 = vg.Length(.707106781202420)
	sinπover6 = vg.Length(.500000000025921)
	cosπover6 = vg.Length(.866025403769473)
)

// SquareGlyph is a glyph that draws the outline of a square.
type SquareGlyph struct{}

// DrawGlyph implements the Glyph interface.
func (SquareGlyph) DrawGlyph(da *DrawArea, sty GlyphStyle, pt Point) {
	da.SetLineStyle(LineStyle{Color: sty.Color, Width: vg.Points(0.5)})
	x := (sty.Radius-sty.Radius*cosπover4)/2 + sty.Radius*cosπover4
	var p vg.Path
	p.Move(pt.X-x, pt.Y-x)
	p.Line(pt.X+x, pt.Y-x)
	p.Line(pt.X+x, pt.Y+x)
Example #21
0
func HandleStats(process string, db *mgo.Database, w http.ResponseWriter, r *http.Request) error {
	r.ParseForm()

	amount := 2
	chartwidth := 800
	chartheight := 600

	if r.Form.Get("amount") != "" {
		pamount, err := strconv.ParseInt(r.Form.Get("amount"), 10, 16)
		if err == nil {
			amount = int(pamount)
		}
	}
	if r.Form.Get("chartwidth") != "" {
		pchartwidth, err := strconv.ParseInt(r.Form.Get("chartwidth"), 10, 16)
		if err == nil {
			chartwidth = int(pchartwidth)
		}
	}
	if r.Form.Get("chartheight") != "" {
		pchartheight, err := strconv.ParseInt(r.Form.Get("chartheight"), 10, 16)
		if err == nil {
			chartheight = int(pchartheight)
		}
	}
	if chartwidth < 40 {
		chartwidth = 40
	}
	if chartheight < 40 {
		chartheight = 40
	}

	q := &info.StatsQuery{
		Process: process,
		Data:    info.SplitParams(r.Form.Get("data")),
		Period:  r.Form.Get("period"),
		Filters: make(map[string]string),
		Groups:  info.SplitParams(r.Form.Get("group")),
		Amount:  amount,
		App:     r.Form.Get("app"),
	}

	output := r.Form.Get("output") // json, chart

	// filters
	for fname, _ := range r.Form {
		if strings.HasPrefix(fname, "f_") {
			q.Filters[strings.TrimPrefix(fname, "f_")] = r.Form.Get(fname)
		}
	}

	res, err := info.QueryStats(db, q)
	if err != nil {
		return err
	}

	if output == "chart" {
		// output chart
		p, err := plot.New()
		if err != nil {
			return err
		}

		p.Title.Text = fmt.Sprintf("Chart %s - %s", r.Form.Get("data"), q.Period)
		p.X.Label.Text = fmt.Sprintf("Day (%s to %s)", res.StartDate.String(), res.EndDate.String())
		//p.X.Tick.Marker = plot.ConstantTicks(res.Ticks())

		for didx, ditem := range q.Data {
			if resinfo, ok := res.Result.(*info.InfoResult); ok {
				resinfo.SetPlotItem(ditem)
				err = InfoAddLinePoints(p, didx, ditem, resinfo)
				if err != nil {
					return err
				}
			} else if resgroup, ok := res.Result.(*info.InfoResultGroup); ok {
				for rgidx, rg := range resgroup.Group {
					rg.SetPlotItem(ditem)
					err = InfoAddLinePoints(p, didx*len(resgroup.Group)+rgidx, ditem+" - "+rg.GroupId, rg)
					if err != nil {
						return err
					}
				}
			}
		}

		w.Header().Add("Content-Type", "image/png")

		width := vg.Length(chartwidth)
		height := vg.Length(chartheight)
		c := vgimg.PngCanvas{vgimg.New(width, height)}
		p.Draw(plot.MakeDrawArea(c))
		c.WriteTo(w)
	} else if output == "table" || output == "atable" {
		w.Header().Add("Content-Type", "text/html; charset=utf-8")

		w.Write([]byte("<table border=\"1\">"))

		for _, ditem := range q.Data {

			w.Write([]byte("<tr>"))
			w.Write([]byte(fmt.Sprintf("<td><b>%s</b></td>", html.EscapeString(ditem))))

			if resinfo, ok := res.Result.(*info.InfoResult); ok {
				resinfo.SetPlotItem(ditem)
				for ridx, rdata := range resinfo.List {
					var dvalue string
					if output == "atable" {
						_, dd := resinfo.XY(ridx)
						if dd != 0 {
							dvalue = fmt.Sprintf("%.1f", dd)
						} else {
							dvalue = "0"
						}
					} else {
						dvalue = fmt.Sprintf("%v", rdata[ditem])
					}
					if dvalue == "0" {
						dvalue = "-"
					}

					w.Write([]byte(fmt.Sprintf("<td align=\"center\">%s</td>", html.EscapeString(dvalue))))
				}

				w.Write([]byte("</tr>"))
			} else if resgroup, ok := res.Result.(*info.InfoResultGroup); ok {
				w.Write([]byte("</tr>"))

				for _, rg := range resgroup.Group {
					w.Write([]byte(fmt.Sprintf("<tr><td>%s</td>", rg.GroupId)))

					rg.SetPlotItem(ditem)
					for ridx, rdata := range rg.List {
						var dvalue string
						if output == "atable" {
							_, dd := rg.XY(ridx)
							if dd != 0 {
								dvalue = fmt.Sprintf("%.1f", dd)
							} else {
								dvalue = "0"
							}
						} else {
							dvalue = fmt.Sprintf("%v", rdata[ditem])
						}
						if dvalue == "0" {
							dvalue = "-"
						}

						w.Write([]byte(fmt.Sprintf("<td align=\"center\">%s</td>", html.EscapeString(dvalue))))
					}

					w.Write([]byte("</tr>"))
				}
			}
		}

		w.Write([]byte("</table>"))
	} else {
		// output json data
		stenc, err := json.Marshal(InfoResponse{ErrorCode: 0, Data: res.Result})
		if err != nil {
			return fmt.Errorf("Error encoding json data: %s", err)
		}

		w.Header().Add("Content-Type", "application/json; charset=utf-8")
		w.Write(stenc)

	}

	return nil
}
Example #22
0
func barchart(path string, data set) error {
	font, err := vg.MakeFont("Helvetica", 10)
	if err != nil {
		return err
	}
	titleFont, err := vg.MakeFont("Helvetica", 12)
	if err != nil {
		return err
	}
	style := plot.TextStyle{Color: color.Gray{0}, Font: font}
	p, err := plot.New()
	if err != nil {
		return err
	}
	p.Title.Text = titles[filter]
	p.Title.TextStyle = plot.TextStyle{Color: color.Gray{0}, Font: titleFont}
	p.X.Label.Text = "Length Offset"
	p.Y.Label.Text = "Relative Frequency"
	p.X.Label.TextStyle = style
	p.Y.Label.TextStyle = style
	p.X.Tick.Label = style
	p.Y.Tick.Label = style
	p.Legend.TextStyle = style

	barsFivePrime, err := plotter.NewBarChart(&normalised{vals: data.fiveEnd}, 1) // A non-zero width is required to prevent the creation failing.
	if err != nil {
		return err
	}
	barsFivePrime.LineStyle.Width = vg.Length(0)
	barsFivePrime.Color = plotutil.Color(0)

	barsThreePrime, err := plotter.NewBarChart(&normalised{vals: data.threeEnd}, 1) // A non-zero width is required to prevent the creation failing.
	if err != nil {
		return err
	}
	barsThreePrime.LineStyle.Width = vg.Length(0)
	barsThreePrime.Color = plotutil.Color(1)

	p.Add(barsFivePrime, barsThreePrime)
	p.Legend.Add("5'-end", barsFivePrime)
	p.Legend.Add("3'-end", barsThreePrime)
	p.Legend.Top = true
	p.NominalX(func() []string {
		n := make([]string, len(data.fiveEnd))
		for i := range data.fiveEnd {
			if v := i - maxLength; v%5 == 0 {
				n[i] = fmt.Sprint(v)
			}
		}
		return n
	}()...)

	c := vgsvg.New(vg.Centimeters(19), vg.Centimeters(10))
	da := plot.MakeDrawArea(c)
	trX, _ := p.Transforms(&da)
	w := ((trX(float64(2*maxLength)) - trX(float64(0))) / vg.Length(2*maxLength)) / 3

	barsFivePrime.Width = w
	barsFivePrime.Offset = -w / 2
	barsThreePrime.Width = w
	barsThreePrime.Offset = w / 2

	p.Draw(da)

	f, err := os.Create(decorate(path, "barchart.svg", filter))
	if err != nil {
		return err
	}
	defer f.Close()
	_, err = c.WriteTo(f)

	return err
}
Example #23
0
func main() {
	flag.Parse()
	if flag.NArg() < 2 {
		usage(os.Args[0])
	}
	filename := flag.Arg(0)
	outputname := flag.Arg(1)
	variables := mapset.NewSet()
	for _, x := range strings.Split(plotvars, ",") {
		if x != "" {
			variables.Add(x)
		}
	}
	expressions := mapset.NewSet()
	for _, x := range strings.Split(expvars, ",") {
		if x != "" {
			expressions.Add(x)
		}
	}
	// variables := mapset.NewSetFromSlice(strings.Split(plotvars, ","))
	// file, err := os.Open(filename, "rb")
	spice_plots, err := raw.ReadFile(filename)
	if err != nil {
		log.Fatal(err)
	}

	var prefix, extension string
	{
		i := strings.LastIndex(outputname, ".")
		prefix, extension = outputname[:i], outputname[i+1:]
	}
	var outplots map[string]*plot.Plot
	for n, plot := range spice_plots {
		fmt.Printf("%s: %s\n", plot.Title, plot.Name)
		// fmt.Printf("%+v\n", plot)
		// outplots := Plot(plots[len(plots)-1])
		if variables.Cardinality() > 0 {
			fmt.Println("Plotting", variables.Cardinality(), "variables:", variables)
			outplots = PlotSome(plot, variables)
		} else {
			outplots = Plot(plot)
		}
		for i, plot := range outplots {
			newname := fmt.Sprintf("%s-%d-%v.%s", prefix, n, i, extension)
			mult := 1.0
			multvg := vg.Length(mult)
			initLineWidth := plot.X.LineStyle.Width
			initFontSize := plot.X.Label.Font.Size
			initTickFontSize := plot.X.Tick.Label.Font.Size
			initTickLength := plot.X.Tick.Width
			plot.X.Label.Font.Size = multvg * initFontSize
			plot.Y.Label.Font.Size = multvg * initFontSize
			plot.X.Tick.Label.Font.Size = multvg * initTickFontSize
			plot.Y.Tick.Label.Font.Size = multvg * initTickFontSize
			plot.X.Tick.Width = multvg * initTickLength
			plot.Y.Tick.Width = multvg * initTickLength
			plot.X.LineStyle.Width = multvg * initLineWidth
			plot.Y.LineStyle.Width = multvg * initLineWidth

			fmt.Println("Outputting ", newname)
			if err := plot.Save(6*mult, 4*mult, newname); err != nil {
				panic(err)
			}
		}
		for _, x := range plot.Vectors {
			fmt.Printf("%s = %v\n", x.Name, x.Get(0))
		}
		if expressions.Cardinality() > 0 { // && plot.NPoints == 1 {
			vt, err := PlotToVariableTable(plot)
			if err != nil {
				log.Fatal(err)
			}
			for x := range expressions.Iter() {
				s := x.(string)
				// If there is an error, just continue.
				if r, err := eval(s, vt); err == nil {
					fmt.Printf("%s = %v\n", s, r)
				}
			}
		}
		fmt.Println()
	}
}
Example #24
0
func boxplot(path string, sets []set) error {
	var (
		fiveEnds  = make([]plotter.Values, len(sets))
		threeEnds = make([]plotter.Values, len(sets))

		err error

		ln int
	)
	for i := range sets {
		fiveEnds[i], err = plotter.CopyValues(&normalised{vals: sets[i].fiveEnd})
		if err != nil {
			return err
		}
		threeEnds[i], err = plotter.CopyValues(&normalised{vals: sets[i].threeEnd})
		if err != nil {
			return err
		}
		if i == 0 {
			ln = fiveEnds[i].Len()
		}
		if fiveEnds[i].Len() != threeEnds[i].Len() || fiveEnds[i].Len() != ln {
			return errors.New("missing values")
		}
	}

	font, err := vg.MakeFont("Helvetica", 10)
	if err != nil {
		return err
	}
	titleFont, err := vg.MakeFont("Helvetica", 12)
	if err != nil {
		return err
	}
	style := plot.TextStyle{Color: color.Gray{0}, Font: font}
	p, err := plot.New()
	if err != nil {
		return err
	}
	p.Title.Text = titles[filter]
	p.Title.TextStyle = plot.TextStyle{Color: color.Gray{0}, Font: titleFont}
	p.X.Label.Text = "Length Offset"
	p.Y.Label.Text = "Relative Frequency"
	p.X.Label.TextStyle = style
	p.Y.Label.TextStyle = style
	p.X.Tick.Label = style
	p.Y.Tick.Label = style
	p.Legend.TextStyle = style

	type boxPair struct{ five, three *plotter.BoxPlot }
	var boxes []boxPair
	for i := 0; i < ln; i++ {
		fiveEnd := make(plotter.Values, len(sets))
		threeEnd := make(plotter.Values, len(sets))
		for j := range sets {
			fiveEnd[j] = fiveEnds[j][i]
			threeEnd[j] = threeEnds[j][i]
		}

		boxFivePrime, err := plotter.NewBoxPlot(1, float64(i), fiveEnd) // A non-zero width is required to prevent the creation failing.
		if err != nil {
			return err
		}
		boxFivePrime.MedianStyle.Width = 0.5
		boxFivePrime.BoxStyle.Width = 0.75
		boxFivePrime.BoxStyle.Color = plotutil.Color(0)

		boxThreePrime, err := plotter.NewBoxPlot(1, float64(i), threeEnd) // A non-zero width is required to prevent the creation failing.
		if err != nil {
			return err
		}
		boxThreePrime.MedianStyle.Width = 0.5
		boxThreePrime.BoxStyle.Width = 0.75
		boxThreePrime.BoxStyle.Color = plotutil.Color(1)

		boxes = append(boxes, boxPair{boxFivePrime, boxThreePrime})

		p.Add(boxFivePrime, boxThreePrime)
	}

	p.Legend.Add("5'-end", &plotter.BarChart{Color: plotutil.Color(0)})
	p.Legend.Add("3'-end", &plotter.BarChart{Color: plotutil.Color(1)})
	p.Legend.Top = true
	p.NominalX(func() []string {
		n := make([]string, ln)
		for i := 0; i < ln; i++ {
			if v := i - maxLength; v%5 == 0 {
				n[i] = fmt.Sprint(v)
			}
		}
		return n
	}()...)
	p.X.Width = 0.5
	p.X.Tick.Width = 0.5
	p.X.Tick.Length = 8
	p.Add(&plotter.Grid{Vertical: plotter.DefaultGridLineStyle})

	c := vgsvg.New(vg.Centimeters(19), vg.Centimeters(10))
	da := plot.MakeDrawArea(c)
	trX, _ := p.Transforms(&da)
	w := ((trX(float64(2*maxLength)) - trX(float64(0))) / vg.Length(2*maxLength)) / 3

	for _, b := range boxes {
		b.five.Width = w
		b.five.Offset = -w / 2
		b.three.Width = w
		b.three.Offset = w / 2
	}

	p.Draw(da)

	f, err := os.Create(decorate(path, "boxplot.svg", filter))
	if err != nil {
		return err
	}
	defer f.Close()
	_, err = c.WriteTo(f)

	return err
}
Example #25
0
func TestBubblesRadius(t *testing.T) {
	b := &Bubbles{
		MinRadius: vg.Length(0),
		MaxRadius: vg.Length(1),
	}

	tests := []struct {
		minz, maxz, z float64
		r             vg.Length
	}{
		{0, 0, 0, vg.Length(0.5)},
		{1, 1, 1, vg.Length(0.5)},
		{0, 1, 0, vg.Length(0)},
		{0, 1, 1, vg.Length(1)},
		{0, 1, 0.5, vg.Length(0.5)},
		{0, 2, 1, vg.Length(0.5)},
		{0, 4, 0, vg.Length(0)},
		{0, 4, 1, vg.Length(0.25)},
		{0, 4, 2, vg.Length(0.5)},
		{0, 4, 3, vg.Length(0.75)},
		{0, 4, 4, vg.Length(1)},
	}

	for _, test := range tests {
		b.MinZ, b.MaxZ = test.minz, test.maxz
		if r := b.radius(test.z); r != test.r {
			t.Errorf("Got incorrect radius (%g) on %v", r, test)
		}
	}
}
Example #26
0
//ScaledRect returns a plotinum Rectangle, appropriately scaled to match the SubPlot's unit rectangle
func (sp SubPlot) ScaledRect(width, height float64) plot.Rect {
	return plot.Rect{
		Min:  plot.Point{vg.Length(sp.Rect.X * width), vg.Length(sp.Rect.Y * height)},
		Size: plot.Point{vg.Length(sp.Rect.Width * width), vg.Length(sp.Rect.Height * height)},
	}
}