diff --git a/pi.go b/pi.go index 34c73e3fc8b044cfd29aa171bae354abbed2beb3..3193c26b794e49b008af4b2ab25252cd9cef88b8 100644 --- a/pi.go +++ b/pi.go @@ -5,7 +5,6 @@ // The plotting library I used does not draw to the screen. Bummer. One could // use the canvas in vggio, although that pulls in all of fyne. - package main import ( @@ -41,6 +40,7 @@ const ( func usage(e string) { u := "usage: pi seed nsteps nstep_nprint" fmt.Fprintln(os.Stderr, e+"\n"+u) + flag.Usage() os.Exit(exitFailure) } @@ -90,6 +90,22 @@ func wrtTable(cmdArgs cmdArgs, pi, stdErr []float32) { } } +// addline saves us doing the same point copying, adding three times. +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)} + } + if line, err := plotter.NewLine(points); err != nil { + return nil, err + } else { + line.LineStyle.Width = 4 + p.Add(line) + } + return p, nil +} + // 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 to the plot function's expectations (x/y pairs). @@ -97,26 +113,53 @@ func doplot(pi, stdErr []float32, plotName string) error { if plotName == "" { return nil } - p := plot.New() - p.Title.Text = "" - cnvs := draw.New(vgimg.New(vg.Points(10), vg.Points(16))) - points := make (plotter.XYs, len(pi)) - for i, piV := range pi { - points[i] = plotter.XY{X: float64(i+1), Y:float64(piV)} + const step = "step" + pPi, err := oneplot(pi) // pi estimate + if err != nil { + return err } - line, err := plotter.NewLine(points) + pPi.Title.Text = "" + pPi.X.Label.Text = step + pPi.Y.Label.Text = "π estimate" + + pStd, err := oneplot(stdErr) // std error if err != nil { return err } - p.X.Label.Text = "step" - p.Y.Label.Text = "π estimate" - p.Add(line) - p.Draw(cnvs) - if plotName != "" { - if err := p.Save(sizeHrznt, sizeVert, plotName); err != nil { - return fmt.Errorf("Plotting, opening \"%s\": %w", plotName, err) + pStd.Title.Text = "" + pStd.X.Label.Text = step + 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 + } + pAbs, err = oneplot(tmperr) + if err != nil { + return err } } + pAbs.X.Label.Text = step + pAbs.Y.Label.Text = "error" + dt := draw.Tiles{Rows: 3, Cols: 1, PadTop: 2} + img := vgimg.New(vg.Points(400), vg.Points(600)) + dc := draw.New(img) + dCnvs := plot.Align([][]*plot.Plot{{pPi}, {pStd}, {pAbs}}, dt, dc) + pPi.Draw(dCnvs[2][0]) + pAbs.Draw(dCnvs[1][0]) + pStd.Draw(dCnvs[0][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 } @@ -161,7 +204,9 @@ func main() { usage(err.Error()) } pi, stdErr := getnums(cmdArgs) - wrtTable(cmdArgs, pi, stdErr) + if cmdArgs.doStdout { + wrtTable(cmdArgs, pi, stdErr) + } if err := doplot(pi, stdErr, cmdArgs.plotName); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(exitFailure)