From 27bfaf16f77be31263b8a2dca7e52eaf9625d817 Mon Sep 17 00:00:00 2001 From: Sebastien Binet Date: Thu, 28 Jan 2016 17:21:58 +0100 Subject: [PATCH 01/11] all: export plot.Axis.SanitizeRange --- axis.go | 4 ++-- plot.go | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/axis.go b/axis.go index cbac5ac4..daf34d26 100644 --- a/axis.go +++ b/axis.go @@ -122,9 +122,9 @@ func makeAxis() (Axis, error) { return a, nil } -// sanitizeRange ensures that the range of the +// SanitizeRange ensures that the range of the // axis makes sense. -func (a *Axis) sanitizeRange() { +func (a *Axis) SanitizeRange() { if math.IsInf(a.Min, 0) { a.Min = 0 } diff --git a/plot.go b/plot.go index 19e52b85..eaa94fb7 100644 --- a/plot.go +++ b/plot.go @@ -153,9 +153,9 @@ func (p *Plot) Draw(c draw.Canvas) { c.Max.Y -= p.Title.Padding } - p.X.sanitizeRange() + p.X.SanitizeRange() x := horizontalAxis{p.X} - p.Y.sanitizeRange() + p.Y.SanitizeRange() y := verticalAxis{p.Y} ywidth := y.size() @@ -179,9 +179,9 @@ func (p *Plot) DataCanvas(da draw.Canvas) draw.Canvas { da.Max.Y -= p.Title.Height(p.Title.Text) - p.Title.Font.Extents().Descent da.Max.Y -= p.Title.Padding } - p.X.sanitizeRange() + p.X.SanitizeRange() x := horizontalAxis{p.X} - p.Y.sanitizeRange() + p.Y.SanitizeRange() y := verticalAxis{p.Y} return padY(p, padX(p, draw.Crop(da, y.size(), x.size(), 0, 0))) } From f620aa1ed37a018fc4538d9308f68542fecd78ce Mon Sep 17 00:00:00 2001 From: Sebastien Binet Date: Thu, 28 Jan 2016 17:26:52 +0100 Subject: [PATCH 02/11] all: export plot.{Horizontal,Vertical}Axis --- axis.go | 28 ++++++++++++++-------------- plot.go | 22 +++++++++++----------- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/axis.go b/axis.go index daf34d26..7f69f658 100644 --- a/axis.go +++ b/axis.go @@ -177,14 +177,14 @@ func (a *Axis) drawTicks() bool { return a.Tick.Width > 0 && a.Tick.Length > 0 } -// A horizontalAxis draws horizontally across the bottom +// A HorizontalAxis draws horizontally across the bottom // of a plot. -type horizontalAxis struct { +type HorizontalAxis struct { Axis } -// size returns the height of the axis. -func (a *horizontalAxis) size() (h vg.Length) { +// Size returns the height of the axis. +func (a *HorizontalAxis) Size() (h vg.Length) { if a.Label.Text != "" { h -= a.Label.Font.Extents().Descent h += a.Label.Height(a.Label.Text) @@ -200,8 +200,8 @@ func (a *horizontalAxis) size() (h vg.Length) { return } -// draw draws the axis along the lower edge of a draw.Canvas. -func (a *horizontalAxis) draw(c draw.Canvas) { +// Draw draws the axis along the lower edge of a draw.Canvas. +func (a *HorizontalAxis) Draw(c draw.Canvas) { y := c.Min.Y if a.Label.Text != "" { y -= a.Label.Font.Extents().Descent @@ -241,7 +241,7 @@ func (a *horizontalAxis) draw(c draw.Canvas) { } // GlyphBoxes returns the GlyphBoxes for the tick labels. -func (a *horizontalAxis) GlyphBoxes(*Plot) (boxes []GlyphBox) { +func (a *HorizontalAxis) GlyphBoxes(*Plot) (boxes []GlyphBox) { for _, t := range a.Tick.Marker.Ticks(a.Min, a.Max) { if t.IsMinor() { continue @@ -259,13 +259,13 @@ func (a *horizontalAxis) GlyphBoxes(*Plot) (boxes []GlyphBox) { return } -// A verticalAxis is drawn vertically up the left side of a plot. -type verticalAxis struct { +// A VerticalAxis is drawn vertically up the left side of a plot. +type VerticalAxis struct { Axis } -// size returns the width of the axis. -func (a *verticalAxis) size() (w vg.Length) { +// Size returns the width of the axis. +func (a *VerticalAxis) Size() (w vg.Length) { if a.Label.Text != "" { w -= a.Label.Font.Extents().Descent w += a.Label.Height(a.Label.Text) @@ -284,8 +284,8 @@ func (a *verticalAxis) size() (w vg.Length) { return } -// draw draws the axis along the left side of a draw.Canvas. -func (a *verticalAxis) draw(c draw.Canvas) { +// Draw draws the axis along the left side of a draw.Canvas. +func (a *VerticalAxis) Draw(c draw.Canvas) { x := c.Min.X if a.Label.Text != "" { x += a.Label.Height(a.Label.Text) @@ -327,7 +327,7 @@ func (a *verticalAxis) draw(c draw.Canvas) { } // GlyphBoxes returns the GlyphBoxes for the tick labels -func (a *verticalAxis) GlyphBoxes(*Plot) (boxes []GlyphBox) { +func (a *VerticalAxis) GlyphBoxes(*Plot) (boxes []GlyphBox) { for _, t := range a.Tick.Marker.Ticks(a.Min, a.Max) { if t.IsMinor() { continue diff --git a/plot.go b/plot.go index eaa94fb7..18e91d26 100644 --- a/plot.go +++ b/plot.go @@ -154,14 +154,14 @@ func (p *Plot) Draw(c draw.Canvas) { } p.X.SanitizeRange() - x := horizontalAxis{p.X} + x := HorizontalAxis{p.X} p.Y.SanitizeRange() - y := verticalAxis{p.Y} + y := VerticalAxis{p.Y} - ywidth := y.size() - x.draw(padX(p, draw.Crop(c, ywidth, 0, 0, 0))) - xheight := x.size() - y.draw(padY(p, draw.Crop(c, 0, 0, xheight, 0))) + ywidth := y.Size() + x.Draw(padX(p, draw.Crop(c, ywidth, 0, 0, 0))) + xheight := x.Size() + y.Draw(padY(p, draw.Crop(c, 0, 0, xheight, 0))) dataC := padY(p, padX(p, draw.Crop(c, ywidth, 0, xheight, 0))) for _, data := range p.plotters { @@ -180,10 +180,10 @@ func (p *Plot) DataCanvas(da draw.Canvas) draw.Canvas { da.Max.Y -= p.Title.Padding } p.X.SanitizeRange() - x := horizontalAxis{p.X} + x := HorizontalAxis{p.X} p.Y.SanitizeRange() - y := verticalAxis{p.Y} - return padY(p, padX(p, draw.Crop(da, y.size(), x.size(), 0, 0))) + y := VerticalAxis{p.Y} + return padY(p, padX(p, draw.Crop(da, y.Size(), x.Size(), 0, 0))) } // DrawGlyphBoxes draws red outlines around the plot's @@ -202,7 +202,7 @@ func (p *Plot) DrawGlyphBoxes(c *draw.Canvas) { func padX(p *Plot, c draw.Canvas) draw.Canvas { glyphs := p.GlyphBoxes(p) l := leftMost(&c, glyphs) - xAxis := horizontalAxis{p.X} + xAxis := HorizontalAxis{p.X} glyphs = append(glyphs, xAxis.GlyphBoxes(p)...) r := rightMost(&c, glyphs) @@ -258,7 +258,7 @@ func leftMost(c *draw.Canvas, boxes []GlyphBox) GlyphBox { func padY(p *Plot, c draw.Canvas) draw.Canvas { glyphs := p.GlyphBoxes(p) b := bottomMost(&c, glyphs) - yAxis := verticalAxis{p.Y} + yAxis := VerticalAxis{p.Y} glyphs = append(glyphs, yAxis.GlyphBoxes(p)...) t := topMost(&c, glyphs) From 9bb24726cba4063834b2df6d2a796a9f45f24e0d Mon Sep 17 00:00:00 2001 From: Sebastien Binet Date: Thu, 28 Jan 2016 17:33:05 +0100 Subject: [PATCH 03/11] all: export plot.Legend.Draw --- export_test.go | 12 ------------ legend.go | 4 ++-- plot.go | 2 +- 3 files changed, 3 insertions(+), 15 deletions(-) delete mode 100644 export_test.go diff --git a/export_test.go b/export_test.go deleted file mode 100644 index 5c4a95f2..00000000 --- a/export_test.go +++ /dev/null @@ -1,12 +0,0 @@ -// 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 plot - -import ( - "github.com/gonum/plot/vg/draw" -) - -// Draw exports the Legend draw method for testing. -func (l *Legend) Draw(c draw.Canvas) { l.draw(c) } diff --git a/legend.go b/legend.go index 8c7abae0..9d5ad2ff 100644 --- a/legend.go +++ b/legend.go @@ -80,8 +80,8 @@ func makeLegend() (Legend, error) { }, nil } -// draw draws the legend to the given draw.Canvas. -func (l *Legend) draw(c draw.Canvas) { +// Draw draws the legend to the given draw.Canvas. +func (l *Legend) Draw(c draw.Canvas) { iconx := c.Min.X textx := iconx + l.ThumbnailWidth + l.TextStyle.Width(" ") xalign := 0.0 diff --git a/plot.go b/plot.go index 18e91d26..81a11544 100644 --- a/plot.go +++ b/plot.go @@ -168,7 +168,7 @@ func (p *Plot) Draw(c draw.Canvas) { data.Plot(dataC, p) } - p.Legend.draw(draw.Crop(draw.Crop(c, ywidth, 0, 0, 0), 0, 0, xheight, 0)) + p.Legend.Draw(draw.Crop(draw.Crop(c, ywidth, 0, 0, 0), 0, 0, xheight, 0)) } // DataCanvas returns a new draw.Canvas that From dae8f1f193890fdc29ff9f3effb3e6fb1814ac67 Mon Sep 17 00:00:00 2001 From: Sebastien Binet Date: Thu, 28 Jan 2016 17:35:35 +0100 Subject: [PATCH 04/11] all: export plot.Pad{X,Y} --- plot.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/plot.go b/plot.go index 81a11544..aa24b65a 100644 --- a/plot.go +++ b/plot.go @@ -159,11 +159,11 @@ func (p *Plot) Draw(c draw.Canvas) { y := VerticalAxis{p.Y} ywidth := y.Size() - x.Draw(padX(p, draw.Crop(c, ywidth, 0, 0, 0))) + x.Draw(PadX(p, draw.Crop(c, ywidth, 0, 0, 0))) xheight := x.Size() - y.Draw(padY(p, draw.Crop(c, 0, 0, xheight, 0))) + y.Draw(PadY(p, draw.Crop(c, 0, 0, xheight, 0))) - dataC := padY(p, padX(p, draw.Crop(c, ywidth, 0, xheight, 0))) + dataC := PadY(p, PadX(p, draw.Crop(c, ywidth, 0, xheight, 0))) for _, data := range p.plotters { data.Plot(dataC, p) } @@ -183,7 +183,7 @@ func (p *Plot) DataCanvas(da draw.Canvas) draw.Canvas { x := HorizontalAxis{p.X} p.Y.SanitizeRange() y := VerticalAxis{p.Y} - return padY(p, padX(p, draw.Crop(da, y.Size(), x.Size(), 0, 0))) + return PadY(p, PadX(p, draw.Crop(da, y.Size(), x.Size(), 0, 0))) } // DrawGlyphBoxes draws red outlines around the plot's @@ -197,9 +197,9 @@ func (p *Plot) DrawGlyphBoxes(c *draw.Canvas) { } } -// padX returns a draw.Canvas that is padded horizontally +// PadX returns a draw.Canvas that is padded horizontally // so that glyphs will no be clipped. -func padX(p *Plot, c draw.Canvas) draw.Canvas { +func PadX(p *Plot, c draw.Canvas) draw.Canvas { glyphs := p.GlyphBoxes(p) l := leftMost(&c, glyphs) xAxis := HorizontalAxis{p.X} @@ -253,9 +253,9 @@ func leftMost(c *draw.Canvas, boxes []GlyphBox) GlyphBox { return l } -// padY returns a draw.Canvas that is padded vertically +// PadY returns a draw.Canvas that is padded vertically // so that glyphs will no be clipped. -func padY(p *Plot, c draw.Canvas) draw.Canvas { +func PadY(p *Plot, c draw.Canvas) draw.Canvas { glyphs := p.GlyphBoxes(p) b := bottomMost(&c, glyphs) yAxis := VerticalAxis{p.Y} From 6465510822a90c0ffb098578a021680f194f4aa9 Mon Sep 17 00:00:00 2001 From: Sebastien Binet Date: Thu, 28 Jan 2016 17:42:42 +0100 Subject: [PATCH 05/11] plot: add PlotDrawer to factor out global plot-style --- gob/gob.go | 2 ++ plot.go | 75 +++++++++++++++++++++++++++++++++++------------------- 2 files changed, 51 insertions(+), 26 deletions(-) diff --git a/gob/gob.go b/gob/gob.go index 60f851f7..4e0df06f 100644 --- a/gob/gob.go +++ b/gob/gob.go @@ -40,4 +40,6 @@ func init() { gob.Register(plotter.XYZs{}) gob.Register(plotter.XYValues{}) + // plot.Drawer + gob.Register(plot.DefaultPlotStyle{}) } diff --git a/plot.go b/plot.go index aa24b65a..1f593ddd 100644 --- a/plot.go +++ b/plot.go @@ -59,6 +59,9 @@ type Plot struct { // Legend is the plot's legend. Legend Legend + // Style draws the plot with style + Style PlotDrawer + // plotters are drawn by calling their Plot method // after the axes are drawn. plotters []Plotter @@ -74,6 +77,11 @@ type Plotter interface { Plot(draw.Canvas, *Plot) } +// PlotDrawer draws a Plot on a draw.Canvas +type PlotDrawer interface { + DrawPlot(p *Plot, c draw.Canvas) +} + // DataRanger wraps the DataRange method. type DataRanger interface { // DataRange returns the range of X and Y values. @@ -104,6 +112,7 @@ func New() (*Plot, error) { X: x, Y: y, Legend: legend, + Style: DefaultPlotStyle{}, } p.Title.TextStyle = draw.TextStyle{ Color: color.Black, @@ -143,32 +152,7 @@ func (p *Plot) Add(ps ...Plotter) { // taken into account when padding the plot so that // none of their glyphs are clipped. func (p *Plot) Draw(c draw.Canvas) { - if p.BackgroundColor != nil { - c.SetColor(p.BackgroundColor) - c.Fill(c.Rectangle.Path()) - } - if p.Title.Text != "" { - c.FillText(p.Title.TextStyle, vg.Point{c.Center().X, c.Max.Y}, -0.5, -1, p.Title.Text) - c.Max.Y -= p.Title.Height(p.Title.Text) - p.Title.Font.Extents().Descent - c.Max.Y -= p.Title.Padding - } - - p.X.SanitizeRange() - x := HorizontalAxis{p.X} - p.Y.SanitizeRange() - y := VerticalAxis{p.Y} - - ywidth := y.Size() - x.Draw(PadX(p, draw.Crop(c, ywidth, 0, 0, 0))) - xheight := x.Size() - y.Draw(PadY(p, draw.Crop(c, 0, 0, xheight, 0))) - - dataC := PadY(p, PadX(p, draw.Crop(c, ywidth, 0, xheight, 0))) - for _, data := range p.plotters { - data.Plot(dataC, p) - } - - p.Legend.Draw(draw.Crop(draw.Crop(c, ywidth, 0, 0, 0), 0, 0, xheight, 0)) + p.Style.DrawPlot(p, c) } // DataCanvas returns a new draw.Canvas that @@ -197,6 +181,45 @@ func (p *Plot) DrawGlyphBoxes(c *draw.Canvas) { } } +// DefaultPlotStyle implements PlotDrawer +type DefaultPlotStyle struct{} + +// Draw draws a plot to a vgdraw.Canvas. +// +// Plotters are drawn in the order in which they were +// added to the plot. Plotters that implement the +// GlyphBoxer interface will have their GlyphBoxes +// taken into account when padding the plot so that +// none of their glyphs are clipped. +func (DefaultPlotStyle) DrawPlot(p *Plot, c draw.Canvas) { + if p.BackgroundColor != nil { + c.SetColor(p.BackgroundColor) + c.Fill(c.Rectangle.Path()) + } + if p.Title.Text != "" { + c.FillText(p.Title.TextStyle, vg.Point{c.Center().X, c.Max.Y}, -0.5, -1, p.Title.Text) + c.Max.Y -= p.Title.Height(p.Title.Text) - p.Title.Font.Extents().Descent + c.Max.Y -= p.Title.Padding + } + + p.X.SanitizeRange() + x := HorizontalAxis{p.X} + p.Y.SanitizeRange() + y := VerticalAxis{p.Y} + + ywidth := y.Size() + x.Draw(PadX(p, draw.Crop(c, ywidth, 0, 0, 0))) + xheight := x.Size() + y.Draw(PadY(p, draw.Crop(c, 0, 0, xheight, 0))) + + dataC := PadY(p, PadX(p, draw.Crop(c, ywidth, 0, xheight, 0))) + for _, data := range p.plotters { + data.Plot(dataC, p) + } + + p.Legend.Draw(draw.Crop(draw.Crop(c, ywidth, 0, 0, 0), 0, 0, xheight, 0)) +} + // PadX returns a draw.Canvas that is padded horizontally // so that glyphs will no be clipped. func PadX(p *Plot, c draw.Canvas) draw.Canvas { From ff309bbf25676e1ed3146c65f00e7325045d022b Mon Sep 17 00:00:00 2001 From: Sebastien Binet Date: Thu, 28 Jan 2016 17:45:32 +0100 Subject: [PATCH 06/11] plot: add plot.*Plot.Plotters() --- plot.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plot.go b/plot.go index 1f593ddd..dc3c0cdc 100644 --- a/plot.go +++ b/plot.go @@ -121,6 +121,11 @@ func New() (*Plot, error) { return p, nil } +// Plotters returns the list of plotters attached to this Plot. +func (p *Plot) Plotters() []Plotter { + return p.plotters +} + // Add adds a Plotters to the plot. // // If the plotters implements DataRanger then the From afcc35de26395d2222f11806f86880ad2e6245b0 Mon Sep 17 00:00:00 2001 From: Sebastien Binet Date: Thu, 28 Jan 2016 17:48:48 +0100 Subject: [PATCH 07/11] plot: godoc fixes --- plot.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plot.go b/plot.go index dc3c0cdc..ead566d3 100644 --- a/plot.go +++ b/plot.go @@ -189,7 +189,7 @@ func (p *Plot) DrawGlyphBoxes(c *draw.Canvas) { // DefaultPlotStyle implements PlotDrawer type DefaultPlotStyle struct{} -// Draw draws a plot to a vgdraw.Canvas. +// DrawPlot draws a plot to a draw.Canvas. // // Plotters are drawn in the order in which they were // added to the plot. Plotters that implement the From c229f60e919f10a097f265f05d8aedc1e05f18a9 Mon Sep 17 00:00:00 2001 From: Sebastien Binet Date: Thu, 28 Jan 2016 18:01:29 +0100 Subject: [PATCH 08/11] examples: add gnuplot/default-style histo --- _examples/run-hist.go | 125 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 _examples/run-hist.go diff --git a/_examples/run-hist.go b/_examples/run-hist.go new file mode 100644 index 00000000..2d3bda11 --- /dev/null +++ b/_examples/run-hist.go @@ -0,0 +1,125 @@ +// +build ignore + +package main + +import ( + "flag" + "image/color" + "math" + "math/rand" + + "github.com/gonum/plot" + "github.com/gonum/plot/plotter" + "github.com/gonum/plot/vg" + "github.com/gonum/plot/vg/draw" +) + +var ( + style = flag.String("style", "gnuplot", "plot style (default|gnuplot)") +) + +func main() { + flag.Parse() + + // Draw some random values from the standard + // normal distribution. + rand.Seed(int64(0)) + v := make(plotter.Values, 10000) + for i := range v { + v[i] = rand.NormFloat64() + } + + // Make a plot and set its title. + p, err := plot.New() + if err != nil { + panic(err) + } + p.Title.Text = "Histogram" + p.X.Label.Text = "X-axis" + p.Y.Label.Text = "Y-axis" + + if *style == "gnuplot" { + p.Style = GnuplotStyle{} + p.X.Padding = 0 + p.Y.Padding = 0 + } + + // Draw a grid behind the data + p.Add(plotter.NewGrid()) + p.Add(plotter.NewGlyphBoxes()) + + // Create a histogram of our values drawn + // from the standard normal. + h, err := plotter.NewHist(v, 16) + if err != nil { + panic(err) + } + // Normalize the area under the histogram to + // sum to one. + h.Normalize(1) + p.Add(h) + + // The normal distribution function + norm := plotter.NewFunction(stdNorm) + norm.Color = color.RGBA{R: 255, A: 255} + norm.Width = vg.Points(2) + p.Add(norm) + + // Save the plot to a PNG file. + if err := p.Save(4, 4, "hist.png"); err != nil { + panic(err) + } +} + +// stdNorm returns the probability of drawing a +// value from a standard normal distribution. +func stdNorm(x float64) float64 { + const sigma = 1.0 + const mu = 0.0 + const root2π = 2.50662827459517818309 + return 1.0 / (sigma * root2π) * math.Exp(-((x-mu)*(x-mu))/(2*sigma*sigma)) +} + +type GnuplotStyle struct{} + +func (s GnuplotStyle) DrawPlot(p *plot.Plot, c draw.Canvas) { + if p.BackgroundColor != nil { + c.SetColor(p.BackgroundColor) + c.Fill(c.Rectangle.Path()) + } + if p.Title.Text != "" { + cx := p.DataCanvas(c) + c.FillText(p.Title.TextStyle, cx.Center().X, c.Max.Y, -0.5, -1, p.Title.Text) + c.Max.Y -= p.Title.Height(p.Title.Text) - p.Title.Font.Extents().Descent + c.Max.Y -= p.Title.Padding + } + + p.X.SanitizeRange() + x := plot.HorizontalAxis{p.X} + p.Y.SanitizeRange() + y := plot.VerticalAxis{p.Y} + + ywidth := y.Size() + xheight := x.Size() + + xda := plot.PadX(p, draw.Crop(c, ywidth-y.Width-y.Padding, 0, 0, 0)) + yda := plot.PadY(p, draw.Crop(c, 0, xheight-x.Width-x.Padding, 0, 0)) + + x.Draw(xda) + y.Draw(yda) + xmin := xda.Min.X + xmax := xda.Max.X + ymin := yda.Min.Y + ymax := xda.Max.Y + xda.StrokeLine2(x.LineStyle, xmin, ymax, xmax, ymax) + xda.StrokeLine2(x.LineStyle, xmin, ymin, xmax, ymin) + yda.StrokeLine2(y.LineStyle, xmin, ymin, xmin, ymax) + yda.StrokeLine2(y.LineStyle, xmax, ymin, xmax, ymax) + + datac := plot.PadY(p, plot.PadX(p, draw.Crop(c, ywidth, xheight, 0, 0))) + for _, data := range p.Plotters() { + data.Plot(datac, p) + } + + p.Legend.Draw(draw.Crop(draw.Crop(c, ywidth, 0, 0, 0), 0, 0, xheight, 0)) +} From 417d9e11129fe1327f940e460a7e841c60404ce9 Mon Sep 17 00:00:00 2001 From: Sebastien Binet Date: Tue, 2 Feb 2016 13:48:27 +0100 Subject: [PATCH 09/11] plotter: migrate example to ExampleHistogramGnuplot --- _examples/run-hist.go | 125 ------------------- plotter/histogram_gnuplotstyle_test.go | 123 ++++++++++++++++++ plotter/testdata/histogram-gnuplot-style.png | Bin 0 -> 12291 bytes 3 files changed, 123 insertions(+), 125 deletions(-) delete mode 100644 _examples/run-hist.go create mode 100644 plotter/histogram_gnuplotstyle_test.go create mode 100644 plotter/testdata/histogram-gnuplot-style.png diff --git a/_examples/run-hist.go b/_examples/run-hist.go deleted file mode 100644 index 2d3bda11..00000000 --- a/_examples/run-hist.go +++ /dev/null @@ -1,125 +0,0 @@ -// +build ignore - -package main - -import ( - "flag" - "image/color" - "math" - "math/rand" - - "github.com/gonum/plot" - "github.com/gonum/plot/plotter" - "github.com/gonum/plot/vg" - "github.com/gonum/plot/vg/draw" -) - -var ( - style = flag.String("style", "gnuplot", "plot style (default|gnuplot)") -) - -func main() { - flag.Parse() - - // Draw some random values from the standard - // normal distribution. - rand.Seed(int64(0)) - v := make(plotter.Values, 10000) - for i := range v { - v[i] = rand.NormFloat64() - } - - // Make a plot and set its title. - p, err := plot.New() - if err != nil { - panic(err) - } - p.Title.Text = "Histogram" - p.X.Label.Text = "X-axis" - p.Y.Label.Text = "Y-axis" - - if *style == "gnuplot" { - p.Style = GnuplotStyle{} - p.X.Padding = 0 - p.Y.Padding = 0 - } - - // Draw a grid behind the data - p.Add(plotter.NewGrid()) - p.Add(plotter.NewGlyphBoxes()) - - // Create a histogram of our values drawn - // from the standard normal. - h, err := plotter.NewHist(v, 16) - if err != nil { - panic(err) - } - // Normalize the area under the histogram to - // sum to one. - h.Normalize(1) - p.Add(h) - - // The normal distribution function - norm := plotter.NewFunction(stdNorm) - norm.Color = color.RGBA{R: 255, A: 255} - norm.Width = vg.Points(2) - p.Add(norm) - - // Save the plot to a PNG file. - if err := p.Save(4, 4, "hist.png"); err != nil { - panic(err) - } -} - -// stdNorm returns the probability of drawing a -// value from a standard normal distribution. -func stdNorm(x float64) float64 { - const sigma = 1.0 - const mu = 0.0 - const root2π = 2.50662827459517818309 - return 1.0 / (sigma * root2π) * math.Exp(-((x-mu)*(x-mu))/(2*sigma*sigma)) -} - -type GnuplotStyle struct{} - -func (s GnuplotStyle) DrawPlot(p *plot.Plot, c draw.Canvas) { - if p.BackgroundColor != nil { - c.SetColor(p.BackgroundColor) - c.Fill(c.Rectangle.Path()) - } - if p.Title.Text != "" { - cx := p.DataCanvas(c) - c.FillText(p.Title.TextStyle, cx.Center().X, c.Max.Y, -0.5, -1, p.Title.Text) - c.Max.Y -= p.Title.Height(p.Title.Text) - p.Title.Font.Extents().Descent - c.Max.Y -= p.Title.Padding - } - - p.X.SanitizeRange() - x := plot.HorizontalAxis{p.X} - p.Y.SanitizeRange() - y := plot.VerticalAxis{p.Y} - - ywidth := y.Size() - xheight := x.Size() - - xda := plot.PadX(p, draw.Crop(c, ywidth-y.Width-y.Padding, 0, 0, 0)) - yda := plot.PadY(p, draw.Crop(c, 0, xheight-x.Width-x.Padding, 0, 0)) - - x.Draw(xda) - y.Draw(yda) - xmin := xda.Min.X - xmax := xda.Max.X - ymin := yda.Min.Y - ymax := xda.Max.Y - xda.StrokeLine2(x.LineStyle, xmin, ymax, xmax, ymax) - xda.StrokeLine2(x.LineStyle, xmin, ymin, xmax, ymin) - yda.StrokeLine2(y.LineStyle, xmin, ymin, xmin, ymax) - yda.StrokeLine2(y.LineStyle, xmax, ymin, xmax, ymax) - - datac := plot.PadY(p, plot.PadX(p, draw.Crop(c, ywidth, xheight, 0, 0))) - for _, data := range p.Plotters() { - data.Plot(datac, p) - } - - p.Legend.Draw(draw.Crop(draw.Crop(c, ywidth, 0, 0, 0), 0, 0, xheight, 0)) -} diff --git a/plotter/histogram_gnuplotstyle_test.go b/plotter/histogram_gnuplotstyle_test.go new file mode 100644 index 00000000..cf76887c --- /dev/null +++ b/plotter/histogram_gnuplotstyle_test.go @@ -0,0 +1,123 @@ +// Copyright ©2016 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 plotter + +import ( + "image/color" + "log" + "math" + "math/rand" + "testing" + + "github.com/gonum/plot" + "github.com/gonum/plot/vg" + "github.com/gonum/plot/vg/draw" +) + +// An example of making a histogram with a custom style. +func ExampleHistogramGnuplot() { + // stdNorm returns the probability of drawing a + // value from a standard normal distribution. + stdNorm := func(x float64) float64 { + const sigma = 1.0 + const mu = 0.0 + const root2π = 2.50662827459517818309 + return 1.0 / (sigma * root2π) * math.Exp(-((x-mu)*(x-mu))/(2*sigma*sigma)) + } + + n := 10000 + vals := make(Values, n) + for i := 0; i < n; i++ { + vals[i] = rand.NormFloat64() + } + + p, err := plot.New() + if err != nil { + log.Panic(err) + } + p.Title.Text = "Histogram" + p.X.Label.Text = "X-axis" + p.Y.Label.Text = "Y-axis" + p.Style = GnuplotStyle{} + + h, err := NewHist(vals, 16) + if err != nil { + log.Panic(err) + } + h.Normalize(1) + p.Add(h) + + // The normal distribution function + norm := NewFunction(stdNorm) + norm.Color = color.RGBA{R: 255, A: 255} + norm.Width = vg.Points(2) + p.Add(norm) + + err = p.Save(200, 200, "testdata/histogram-gnuplot-style.png") + if err != nil { + log.Panic(err) + } +} + +func TestHistogramGnuplot(t *testing.T) { + checkPlot(ExampleHistogramGnuplot, t, "histogram-gnuplot-style.png") +} + +type GnuplotStyle struct{} + +func (s GnuplotStyle) DrawPlot(p *plot.Plot, c draw.Canvas) { + if p.BackgroundColor != nil { + c.SetColor(p.BackgroundColor) + c.Fill(c.Rectangle.Path()) + } + + { + xpad := p.X.Padding + ypad := p.Y.Padding + defer func() { + p.X.Padding = xpad + p.Y.Padding = ypad + }() + } + + p.X.Padding = 0 + p.Y.Padding = 0 + + p.X.SanitizeRange() + p.Y.SanitizeRange() + xaxis := plot.HorizontalAxis{p.X} + yaxis := plot.VerticalAxis{p.Y} + + ywidth := yaxis.Size() + xheight := xaxis.Size() + + if p.Title.Text != "" { + cx := draw.Crop(c, ywidth, 0, 0, 0) + c.FillText(p.Title.TextStyle, cx.Center().X, c.Max.Y, -0.5, -1, p.Title.Text) + c.Max.Y -= p.Title.Height(p.Title.Text) - p.Title.Font.Extents().Descent + c.Max.Y -= p.Title.Padding + } + + xc := plot.PadX(p, draw.Crop(c, ywidth-yaxis.Width-yaxis.Padding, 0, 0, 0)) + yc := plot.PadY(p, draw.Crop(c, 0, xheight-xaxis.Width-xaxis.Padding, xheight, 0)) + + xaxis.Draw(xc) + yaxis.Draw(yc) + xmin := xc.Min.X + xmax := xc.Max.X + ymin := yc.Min.Y + ymax := xc.Max.Y + xc.StrokeLine2(xaxis.LineStyle, xmin, ymax, xmax, ymax) + xc.StrokeLine2(xaxis.LineStyle, xmin, ymin, xmax, ymin) + yc.StrokeLine2(yaxis.LineStyle, xmin, ymin, xmin, ymax) + yc.StrokeLine2(yaxis.LineStyle, xmax, ymin, xmax, ymax) + + datac := plot.PadY(p, plot.PadX(p, draw.Crop(c, ywidth, 0, xheight, 0))) + for _, data := range p.Plotters() { + data.Plot(datac, p) + } + + p.Legend.Draw(draw.Crop(draw.Crop(c, ywidth, 0, 0, 0), 0, 0, xheight, 0)) +} diff --git a/plotter/testdata/histogram-gnuplot-style.png b/plotter/testdata/histogram-gnuplot-style.png new file mode 100644 index 0000000000000000000000000000000000000000..8c2df6a60977be95c0f6c71284d200be8ba48377 GIT binary patch literal 12291 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 Date: Mon, 4 Apr 2016 17:10:55 +0200 Subject: [PATCH 10/11] plotter: cosmetics --- plotter/histogram_gnuplotstyle_test.go | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/plotter/histogram_gnuplotstyle_test.go b/plotter/histogram_gnuplotstyle_test.go index cf76887c..bf91e9bb 100644 --- a/plotter/histogram_gnuplotstyle_test.go +++ b/plotter/histogram_gnuplotstyle_test.go @@ -73,14 +73,12 @@ func (s GnuplotStyle) DrawPlot(p *plot.Plot, c draw.Canvas) { c.Fill(c.Rectangle.Path()) } - { - xpad := p.X.Padding - ypad := p.Y.Padding - defer func() { - p.X.Padding = xpad - p.Y.Padding = ypad - }() - } + xpad := p.X.Padding + ypad := p.Y.Padding + defer func() { + p.X.Padding = xpad + p.Y.Padding = ypad + }() p.X.Padding = 0 p.Y.Padding = 0 From c9e6ee6b18f0d9adbefa82edf2521f041a382f79 Mon Sep 17 00:00:00 2001 From: Sebastien Binet Date: Wed, 6 Jul 2016 18:14:45 +0200 Subject: [PATCH 11/11] plotter: fix custom plot style example --- ...le_test.go => histogram_customstyle_test.go} | 16 ++++++++-------- ...le.png => histogram-custom-style_golden.png} | Bin 2 files changed, 8 insertions(+), 8 deletions(-) rename plotter/{histogram_gnuplotstyle_test.go => histogram_customstyle_test.go} (85%) rename plotter/testdata/{histogram-gnuplot-style.png => histogram-custom-style_golden.png} (100%) diff --git a/plotter/histogram_gnuplotstyle_test.go b/plotter/histogram_customstyle_test.go similarity index 85% rename from plotter/histogram_gnuplotstyle_test.go rename to plotter/histogram_customstyle_test.go index bf91e9bb..e66605ff 100644 --- a/plotter/histogram_gnuplotstyle_test.go +++ b/plotter/histogram_customstyle_test.go @@ -17,7 +17,7 @@ import ( ) // An example of making a histogram with a custom style. -func ExampleHistogramGnuplot() { +func ExampleHistogramCustomStyle() { // stdNorm returns the probability of drawing a // value from a standard normal distribution. stdNorm := func(x float64) float64 { @@ -40,7 +40,7 @@ func ExampleHistogramGnuplot() { p.Title.Text = "Histogram" p.X.Label.Text = "X-axis" p.Y.Label.Text = "Y-axis" - p.Style = GnuplotStyle{} + p.Style = CustomStyle{} h, err := NewHist(vals, 16) if err != nil { @@ -55,19 +55,19 @@ func ExampleHistogramGnuplot() { norm.Width = vg.Points(2) p.Add(norm) - err = p.Save(200, 200, "testdata/histogram-gnuplot-style.png") + err = p.Save(200, 200, "testdata/histogram-custom-style.png") if err != nil { log.Panic(err) } } -func TestHistogramGnuplot(t *testing.T) { - checkPlot(ExampleHistogramGnuplot, t, "histogram-gnuplot-style.png") +func TestHistogramCustomStyle(t *testing.T) { + checkPlot(ExampleHistogramCustomStyle, t, "histogram-custom-style.png") } -type GnuplotStyle struct{} +type CustomStyle struct{} -func (s GnuplotStyle) DrawPlot(p *plot.Plot, c draw.Canvas) { +func (CustomStyle) DrawPlot(p *plot.Plot, c draw.Canvas) { if p.BackgroundColor != nil { c.SetColor(p.BackgroundColor) c.Fill(c.Rectangle.Path()) @@ -93,7 +93,7 @@ func (s GnuplotStyle) DrawPlot(p *plot.Plot, c draw.Canvas) { if p.Title.Text != "" { cx := draw.Crop(c, ywidth, 0, 0, 0) - c.FillText(p.Title.TextStyle, cx.Center().X, c.Max.Y, -0.5, -1, p.Title.Text) + c.FillText(p.Title.TextStyle, vg.Point{cx.Center().X, c.Max.Y}, -0.5, -1, p.Title.Text) c.Max.Y -= p.Title.Height(p.Title.Text) - p.Title.Font.Extents().Descent c.Max.Y -= p.Title.Padding } diff --git a/plotter/testdata/histogram-gnuplot-style.png b/plotter/testdata/histogram-custom-style_golden.png similarity index 100% rename from plotter/testdata/histogram-gnuplot-style.png rename to plotter/testdata/histogram-custom-style_golden.png