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  )