diff --git a/doplot.go b/doplot.go new file mode 100644 index 0000000000000000000000000000000000000000..c674b3065cc1e7c9152251ba1a01e0da5f2dff4f --- /dev/null +++ b/doplot.go @@ -0,0 +1,100 @@ +// 4 May 2021 +// This does the plots for the Uebung. +// doplot makes a primitive plot without any interesting options +// If no plotname is given, it means no plot is wanted. +// We have to rewrite the data to fit into their format. + +package main + +import ( + "fmt" + "math" + "os" + + "gonum.org/v1/plot" + "gonum.org/v1/plot/font" + "gonum.org/v1/plot/plotter" + "gonum.org/v1/plot/vg" + "gonum.org/v1/plot/vg/draw" + "gonum.org/v1/plot/vg/vgimg" +) + +const ( // plot size.. my arbitrary choice + pSizeHrznt font.Length = 20 * vg.Centimeter + pSizeVert font.Length = 28 * vg.Centimeter +) + +// oneplot saves us doing the same point copying, adding three times. +// I also toss out the first two points. They are a bit silly and +// have massive errors. +func oneplot(y []float32) (*plot.Plot, error) { + p := plot.New() + points := make(plotter.XYs, len(y)) + for i, piV := range y { + points[i] = plotter.XY{X: float64(i + 1), Y: float64(piV)} + } // Toss out the first two y-values... + points[0].Y, points[1].Y = points[2].Y, points[2].Y + if line, err := plotter.NewScatter(points); err != nil { + return nil, err + } else { + p.Add(line) + } + return p, nil +} + +// doplot copies the data and calls the plotters and writes a jpg +// file to plotname. If plotname has not been set, we just return. +// It is not an error. +func doplot(pi, stdErr []float32, plotName string) error { + if plotName == "" { + return nil + } + const step = "step" + var pPi, pStd, pAbs *plot.Plot + var err error + if pPi, err = oneplot(pi); err != nil { // pi estimate + return err + } + pPi.Title.Text = "π estimate" + pPi.Y.Label.Text = "π" + + if pStd, err = oneplot(stdErr); err != nil { // std error + return err + } + pStd.Title.Text = "standard error" + pStd.Y.Label.Text = "std error" + + pAbs = plot.New() + { + tmperr := make([]float32, len(pi)) // the extra bracing makes + for i := range tmperr { // the lifetime of tmperr clear + tmperr[i] = pi[i] - math.Pi // for garbage collector + } + if pAbs, err = oneplot(tmperr); err != nil { + return err + } + } + pAbs.Title.Text = "absolute error" + pAbs.Y.Label.Text = "error" + + pStd.X.Label.Text, pAbs.X.Label.Text, pPi.X.Label.Text = step, step, step + dt := draw.Tiles{Rows: 3, Cols: 1, PadTop: 2} + img := vgimg.New(pSizeHrznt, pSizeVert) + + dc := draw.New(img) + dCnvs := plot.Align([][]*plot.Plot{{pPi}, {pStd}, {pAbs}}, dt, dc) + + pPi.Draw(dCnvs[0][0]) + pStd.Draw(dCnvs[1][0]) + pAbs.Draw(dCnvs[2][0]) + w, err := os.Create(plotName) + if err != nil { + return fmt.Errorf("Opening plotfile for writing %w", err) + } + defer w.Close() + jpg := vgimg.JpegCanvas{Canvas: img} + if _, err := jpg.WriteTo(w); err != nil { + return err + } + return nil +}