382 lines
9.3 KiB
Go
382 lines
9.3 KiB
Go
// Copyright ©2015 The Gonum Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package plotutil
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
|
|
"gonum.org/v1/plot"
|
|
"gonum.org/v1/plot/plotter"
|
|
"gonum.org/v1/plot/vg"
|
|
)
|
|
|
|
type combineXYs struct{ xs, ys plotter.Valuer }
|
|
|
|
func (c combineXYs) Len() int { return c.xs.Len() }
|
|
func (c combineXYs) XY(i int) (float64, float64) { return c.xs.Value(i), c.ys.Value(i) }
|
|
|
|
type item struct {
|
|
name string
|
|
value plot.Thumbnailer
|
|
}
|
|
|
|
// AddStackedAreaPlots adds stacked area plot plotters to a plot.
|
|
// The variadic arguments must be either strings
|
|
// or plotter.Valuers. Each valuer adds a stacked area
|
|
// plot to the plot below the stacked area plots added
|
|
// before it. If a plotter.Valuer is immediately
|
|
// preceeded by a string then the string value is used to
|
|
// label the legend.
|
|
// Plots should be added in order of tallest to shortest,
|
|
// because they will be drawn in the order they are added
|
|
// (i.e. later plots will be painted over earlier plots).
|
|
//
|
|
// If an error occurs then none of the plotters are added
|
|
// to the plot, and the error is returned.
|
|
func AddStackedAreaPlots(plt *plot.Plot, xs plotter.Valuer, vs ...interface{}) error {
|
|
var ps []plot.Plotter
|
|
var names []item
|
|
name := ""
|
|
var i int
|
|
|
|
for _, v := range vs {
|
|
switch t := v.(type) {
|
|
case string:
|
|
name = t
|
|
|
|
case plotter.Valuer:
|
|
if xs.Len() != t.Len() {
|
|
return errors.New("X/Y length mismatch")
|
|
}
|
|
|
|
// Make a line plotter and set its style.
|
|
l, err := plotter.NewLine(combineXYs{xs: xs, ys: t})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
l.LineStyle.Width = vg.Points(0)
|
|
color := Color(i)
|
|
i++
|
|
l.FillColor = color
|
|
|
|
ps = append(ps, l)
|
|
|
|
if name != "" {
|
|
names = append(names, item{name: name, value: l})
|
|
name = ""
|
|
}
|
|
|
|
default:
|
|
panic(fmt.Sprintf("plotutil: AddStackedAreaPlots handles strings and plotter.Valuers, got %T", t))
|
|
}
|
|
}
|
|
|
|
plt.Add(ps...)
|
|
for _, v := range names {
|
|
plt.Legend.Add(v.name, v.value)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// AddBoxPlots adds box plot plotters to a plot and
|
|
// sets the X axis of the plot to be nominal.
|
|
// The variadic arguments must be either strings
|
|
// or plotter.Valuers. Each valuer adds a box plot
|
|
// to the plot at the X location corresponding to
|
|
// the number of box plots added before it. If a
|
|
// plotter.Valuer is immediately preceeded by a
|
|
// string then the string value is used to label the
|
|
// tick mark for the box plot's X location.
|
|
//
|
|
// If an error occurs then none of the plotters are added
|
|
// to the plot, and the error is returned.
|
|
func AddBoxPlots(plt *plot.Plot, width vg.Length, vs ...interface{}) error {
|
|
var ps []plot.Plotter
|
|
var names []string
|
|
name := ""
|
|
for _, v := range vs {
|
|
switch t := v.(type) {
|
|
case string:
|
|
name = t
|
|
|
|
case plotter.Valuer:
|
|
b, err := plotter.NewBoxPlot(width, float64(len(names)), t)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ps = append(ps, b)
|
|
names = append(names, name)
|
|
name = ""
|
|
|
|
default:
|
|
panic(fmt.Sprintf("plotutil: AddBoxPlots handles strings and plotter.Valuers, got %T", t))
|
|
}
|
|
}
|
|
plt.Add(ps...)
|
|
plt.NominalX(names...)
|
|
return nil
|
|
}
|
|
|
|
// AddScatters adds Scatter plotters to a plot.
|
|
// The variadic arguments must be either strings
|
|
// or plotter.XYers. Each plotter.XYer is added to
|
|
// the plot using the next color, and glyph shape
|
|
// via the Color and Shape functions. If a
|
|
// plotter.XYer is immediately preceeded by
|
|
// a string then a legend entry is added to the plot
|
|
// using the string as the name.
|
|
//
|
|
// If an error occurs then none of the plotters are added
|
|
// to the plot, and the error is returned.
|
|
func AddScatters(plt *plot.Plot, vs ...interface{}) error {
|
|
var ps []plot.Plotter
|
|
var items []item
|
|
name := ""
|
|
var i int
|
|
for _, v := range vs {
|
|
switch t := v.(type) {
|
|
case string:
|
|
name = t
|
|
|
|
case plotter.XYer:
|
|
s, err := plotter.NewScatter(t)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
s.Color = Color(i)
|
|
s.Shape = Shape(i)
|
|
i++
|
|
ps = append(ps, s)
|
|
if name != "" {
|
|
items = append(items, item{name: name, value: s})
|
|
name = ""
|
|
}
|
|
|
|
default:
|
|
panic(fmt.Sprintf("plotutil: AddScatters handles strings and plotter.XYers, got %T", t))
|
|
}
|
|
}
|
|
plt.Add(ps...)
|
|
for _, v := range items {
|
|
plt.Legend.Add(v.name, v.value)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// AddLines adds Line plotters to a plot.
|
|
// The variadic arguments must be a string
|
|
// or one of a plotting type, plotter.XYers or *plotter.Function.
|
|
// Each plotting type is added to
|
|
// the plot using the next color and dashes
|
|
// shape via the Color and Dashes functions.
|
|
// If a plotting type is immediately preceeded by
|
|
// a string then a legend entry is added to the plot
|
|
// using the string as the name.
|
|
//
|
|
// If an error occurs then none of the plotters are added
|
|
// to the plot, and the error is returned.
|
|
func AddLines(plt *plot.Plot, vs ...interface{}) error {
|
|
var ps []plot.Plotter
|
|
var items []item
|
|
name := ""
|
|
var i int
|
|
for _, v := range vs {
|
|
switch t := v.(type) {
|
|
case string:
|
|
name = t
|
|
|
|
case plotter.XYer:
|
|
l, err := plotter.NewLine(t)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
l.Color = Color(i)
|
|
l.Dashes = Dashes(i)
|
|
i++
|
|
ps = append(ps, l)
|
|
if name != "" {
|
|
items = append(items, item{name: name, value: l})
|
|
name = ""
|
|
}
|
|
|
|
case *plotter.Function:
|
|
t.Color = Color(i)
|
|
t.Dashes = Dashes(i)
|
|
i++
|
|
ps = append(ps, t)
|
|
if name != "" {
|
|
items = append(items, item{name: name, value: t})
|
|
name = ""
|
|
}
|
|
|
|
default:
|
|
panic(fmt.Sprintf("plotutil: AddLines handles strings, plotter.XYers and *plotter.Function, got %T", t))
|
|
}
|
|
}
|
|
plt.Add(ps...)
|
|
for _, v := range items {
|
|
plt.Legend.Add(v.name, v.value)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// AddLinePoints adds Line and Scatter plotters to a
|
|
// plot. The variadic arguments must be either strings
|
|
// or plotter.XYers. Each plotter.XYer is added to
|
|
// the plot using the next color, dashes, and glyph
|
|
// shape via the Color, Dashes, and Shape functions.
|
|
// If a plotter.XYer is immediately preceeded by
|
|
// a string then a legend entry is added to the plot
|
|
// using the string as the name.
|
|
//
|
|
// If an error occurs then none of the plotters are added
|
|
// to the plot, and the error is returned.
|
|
func AddLinePoints(plt *plot.Plot, vs ...interface{}) error {
|
|
var ps []plot.Plotter
|
|
type item struct {
|
|
name string
|
|
value [2]plot.Thumbnailer
|
|
}
|
|
var items []item
|
|
name := ""
|
|
var i int
|
|
for _, v := range vs {
|
|
switch t := v.(type) {
|
|
case string:
|
|
name = t
|
|
|
|
case plotter.XYer:
|
|
l, s, err := plotter.NewLinePoints(t)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
l.Color = Color(i)
|
|
l.Dashes = Dashes(i)
|
|
s.Color = Color(i)
|
|
s.Shape = Shape(i)
|
|
i++
|
|
ps = append(ps, l, s)
|
|
if name != "" {
|
|
items = append(items, item{name: name, value: [2]plot.Thumbnailer{l, s}})
|
|
name = ""
|
|
}
|
|
|
|
default:
|
|
panic(fmt.Sprintf("plotutil: AddLinePoints handles strings and plotter.XYers, got %T", t))
|
|
}
|
|
}
|
|
plt.Add(ps...)
|
|
for _, item := range items {
|
|
v := item.value[:]
|
|
plt.Legend.Add(item.name, v[0], v[1])
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// AddErrorBars adds XErrorBars and YErrorBars
|
|
// to a plot. The variadic arguments must be
|
|
// of type plotter.XYer, and must be either a
|
|
// plotter.XErrorer, plotter.YErrorer, or both.
|
|
// Each errorer is added to the plot the color from
|
|
// the Colors function corresponding to its position
|
|
// in the argument list.
|
|
//
|
|
// If an error occurs then none of the plotters are added
|
|
// to the plot, and the error is returned.
|
|
func AddErrorBars(plt *plot.Plot, vs ...interface{}) error {
|
|
var ps []plot.Plotter
|
|
for i, v := range vs {
|
|
added := false
|
|
|
|
if xerr, ok := v.(interface {
|
|
plotter.XYer
|
|
plotter.XErrorer
|
|
}); ok {
|
|
e, err := plotter.NewXErrorBars(xerr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
e.Color = Color(i)
|
|
ps = append(ps, e)
|
|
added = true
|
|
}
|
|
|
|
if yerr, ok := v.(interface {
|
|
plotter.XYer
|
|
plotter.YErrorer
|
|
}); ok {
|
|
e, err := plotter.NewYErrorBars(yerr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
e.Color = Color(i)
|
|
ps = append(ps, e)
|
|
added = true
|
|
}
|
|
|
|
if added {
|
|
continue
|
|
}
|
|
panic(fmt.Sprintf("plotutil: AddErrorBars expects plotter.XErrorer or plotter.YErrorer, got %T", v))
|
|
}
|
|
plt.Add(ps...)
|
|
return nil
|
|
}
|
|
|
|
// AddXErrorBars adds XErrorBars to a plot.
|
|
// The variadic arguments must be
|
|
// of type plotter.XYer, and plotter.XErrorer.
|
|
// Each errorer is added to the plot the color from
|
|
// the Colors function corresponding to its position
|
|
// in the argument list.
|
|
//
|
|
// If an error occurs then none of the plotters are added
|
|
// to the plot, and the error is returned.
|
|
func AddXErrorBars(plt *plot.Plot, es ...interface {
|
|
plotter.XYer
|
|
plotter.XErrorer
|
|
}) error {
|
|
var ps []plot.Plotter
|
|
for i, e := range es {
|
|
bars, err := plotter.NewXErrorBars(e)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
bars.Color = Color(i)
|
|
ps = append(ps, bars)
|
|
}
|
|
plt.Add(ps...)
|
|
return nil
|
|
}
|
|
|
|
// AddYErrorBars adds YErrorBars to a plot.
|
|
// The variadic arguments must be
|
|
// of type plotter.XYer, and plotter.YErrorer.
|
|
// Each errorer is added to the plot the color from
|
|
// the Colors function corresponding to its position
|
|
// in the argument list.
|
|
//
|
|
// If an error occurs then none of the plotters are added
|
|
// to the plot, and the error is returned.
|
|
func AddYErrorBars(plt *plot.Plot, es ...interface {
|
|
plotter.XYer
|
|
plotter.YErrorer
|
|
}) error {
|
|
var ps []plot.Plotter
|
|
for i, e := range es {
|
|
bars, err := plotter.NewYErrorBars(e)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
bars.Color = Color(i)
|
|
ps = append(ps, bars)
|
|
}
|
|
plt.Add(ps...)
|
|
return nil
|
|
}
|