go-hep.org/x/hep@v0.38.1/hplot/binerrband.go (about) 1 // Copyright ©2020 The go-hep Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package hplot 6 7 import ( 8 "image/color" 9 "math" 10 11 "go-hep.org/x/hep/hbook" 12 13 "gonum.org/v1/plot" 14 "gonum.org/v1/plot/plotter" 15 "gonum.org/v1/plot/vg/draw" 16 ) 17 18 // BinnedErrBand implements the plot.Plotter interface, 19 // drawing a colored band for the error on any binned 20 // quantity. 21 type BinnedErrBand struct { 22 23 // Data for every bins. 24 Counts []hbook.Count 25 26 // LineStyle is the style of the line 27 // contouring the band. 28 // Use zero width to disable. 29 draw.LineStyle 30 31 // FillColor is the color to fill the area 32 // between the top and bottom data points. 33 // Use nil to disable the filling. 34 FillColor color.Color 35 36 // LogY allows rendering with a log-scaled Y axis. 37 // When enabled, bins with negative or zero minimal value (val-err) 38 // will be discarded from the error band. 39 // The lowest Y value for the DataRange will be corrected to leave an 40 // arbitrary amount of height for the smallest bin entry so it is visible 41 // on the final plot. 42 LogY bool 43 } 44 45 // NewBinnedErrBand creates a binned error band 46 // from a slice of count. 47 func NewBinnedErrBand(cs []hbook.Count) *BinnedErrBand { 48 return &BinnedErrBand{ 49 Counts: cs, 50 } 51 } 52 53 // Plot implements the Plotter interface, 54 // drawing a colored box defined by width 55 // of bins (x-axis) and error (y-axis). 56 func (b *BinnedErrBand) Plot(c draw.Canvas, plt *plot.Plot) { 57 58 for _, count := range b.Counts { 59 60 // Get four corner of the ith bin 61 xmin, xmax := count.XRange.Min, count.XRange.Max 62 y, ydo, yup := count.Val, count.Err.Low, count.Err.High 63 64 // If log scale disregard negative values. 65 if b.LogY && y-ydo <= 0 { 66 continue 67 } 68 69 // Polygon 70 xys := plotter.XYs{ 71 plotter.XY{X: xmin, Y: y - ydo}, 72 plotter.XY{X: xmin, Y: y + yup}, 73 plotter.XY{X: xmax, Y: y + yup}, 74 plotter.XY{X: xmax, Y: y - ydo}, 75 } 76 poly := plotter.Polygon{XYs: []plotter.XYs{xys}, Color: b.FillColor} 77 poly.Plot(c, plt) 78 79 // Bottom line 80 xysBo := plotter.XYs{xys[0], xys[3]} 81 lBo := plotter.Line{XYs: xysBo, LineStyle: b.LineStyle} 82 lBo.Plot(c, plt) 83 84 // Upper line 85 xysUp := plotter.XYs{xys[1], xys[2]} 86 lUp := plotter.Line{XYs: xysUp, LineStyle: b.LineStyle} 87 lUp.Plot(c, plt) 88 } 89 } 90 91 // DataRange returns the minimum and maximum x and 92 // y values, implementing the plot.DataRanger interface. 93 func (b *BinnedErrBand) DataRange() (xmin, xmax, ymin, ymax float64) { 94 95 n := len(b.Counts) - 1 96 xmin, xmax = b.Counts[0].XRange.Min, b.Counts[n].XRange.Max 97 98 ymin, ymax = math.Inf(+1), math.Inf(-1) 99 for _, c := range b.Counts { 100 y, ydo, yup := c.Val, c.Err.Low, c.Err.High 101 if !(y-ydo <= 0 && b.LogY) { 102 ymin = math.Min(ymin, y-ydo) 103 } 104 ymax = math.Max(ymax, y+yup) 105 } 106 107 return xmin, xmax, ymin, ymax 108 } 109 110 var ( 111 _ plot.Plotter = (*BinnedErrBand)(nil) 112 _ plot.DataRanger = (*BinnedErrBand)(nil) 113 )