...

Source file src/gonum.org/v1/plot/plotter/errbars.go

Documentation: gonum.org/v1/plot/plotter

     1  // Copyright ©2015 The Gonum 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 plotter
     6  
     7  import (
     8  	"math"
     9  
    10  	"gonum.org/v1/plot"
    11  	"gonum.org/v1/plot/vg"
    12  	"gonum.org/v1/plot/vg/draw"
    13  )
    14  
    15  // DefaultCapWidth is the default width of error bar caps.
    16  var DefaultCapWidth = vg.Points(5)
    17  
    18  // YErrorBars implements the plot.Plotter, plot.DataRanger,
    19  // and plot.GlyphBoxer interfaces, drawing vertical error
    20  // bars, denoting error in Y values.
    21  type YErrorBars struct {
    22  	XYs
    23  
    24  	// YErrors is a copy of the Y errors for each point.
    25  	YErrors
    26  
    27  	// LineStyle is the style used to draw the error bars.
    28  	draw.LineStyle
    29  
    30  	// CapWidth is the width of the caps drawn at the top
    31  	// of each error bar.
    32  	CapWidth vg.Length
    33  }
    34  
    35  // NewYErrorBars returns a new YErrorBars plotter, or an error on failure.
    36  // The error values from the YErrorer interface are interpreted as relative
    37  // to the corresponding Y value. The errors for a given Y value are computed
    38  // by taking the absolute value of the error returned by the YErrorer
    39  // and subtracting the first and adding the second to the Y value.
    40  func NewYErrorBars(yerrs interface {
    41  	XYer
    42  	YErrorer
    43  }) (*YErrorBars, error) {
    44  
    45  	errors := make(YErrors, yerrs.Len())
    46  	for i := range errors {
    47  		errors[i].Low, errors[i].High = yerrs.YError(i)
    48  		if err := CheckFloats(errors[i].Low, errors[i].High); err != nil {
    49  			return nil, err
    50  		}
    51  	}
    52  	xys, err := CopyXYs(yerrs)
    53  	if err != nil {
    54  		return nil, err
    55  	}
    56  
    57  	return &YErrorBars{
    58  		XYs:       xys,
    59  		YErrors:   errors,
    60  		LineStyle: DefaultLineStyle,
    61  		CapWidth:  DefaultCapWidth,
    62  	}, nil
    63  }
    64  
    65  // Plot implements the Plotter interface, drawing labels.
    66  func (e *YErrorBars) Plot(c draw.Canvas, p *plot.Plot) {
    67  	trX, trY := p.Transforms(&c)
    68  	for i, err := range e.YErrors {
    69  		x := trX(e.XYs[i].X)
    70  		ylow := trY(e.XYs[i].Y - math.Abs(err.Low))
    71  		yhigh := trY(e.XYs[i].Y + math.Abs(err.High))
    72  
    73  		bar := c.ClipLinesY([]vg.Point{{X: x, Y: ylow}, {X: x, Y: yhigh}})
    74  		c.StrokeLines(e.LineStyle, bar...)
    75  		e.drawCap(&c, x, ylow)
    76  		e.drawCap(&c, x, yhigh)
    77  	}
    78  }
    79  
    80  // drawCap draws the cap if it is not clipped.
    81  func (e *YErrorBars) drawCap(c *draw.Canvas, x, y vg.Length) {
    82  	if !c.Contains(vg.Point{X: x, Y: y}) {
    83  		return
    84  	}
    85  	c.StrokeLine2(e.LineStyle, x-e.CapWidth/2, y, x+e.CapWidth/2, y)
    86  }
    87  
    88  // DataRange implements the plot.DataRanger interface.
    89  func (e *YErrorBars) DataRange() (xmin, xmax, ymin, ymax float64) {
    90  	xmin, xmax = Range(XValues{e})
    91  	ymin = math.Inf(1)
    92  	ymax = math.Inf(-1)
    93  	for i, err := range e.YErrors {
    94  		y := e.XYs[i].Y
    95  		ylow := y - math.Abs(err.Low)
    96  		yhigh := y + math.Abs(err.High)
    97  		ymin = math.Min(math.Min(math.Min(ymin, y), ylow), yhigh)
    98  		ymax = math.Max(math.Max(math.Max(ymax, y), ylow), yhigh)
    99  	}
   100  	return
   101  }
   102  
   103  // GlyphBoxes implements the plot.GlyphBoxer interface.
   104  func (e *YErrorBars) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox {
   105  	rect := vg.Rectangle{
   106  		Min: vg.Point{
   107  			X: -e.CapWidth / 2,
   108  			Y: -e.LineStyle.Width / 2,
   109  		},
   110  		Max: vg.Point{
   111  			X: +e.CapWidth / 2,
   112  			Y: +e.LineStyle.Width / 2,
   113  		},
   114  	}
   115  	var bs []plot.GlyphBox
   116  	for i, err := range e.YErrors {
   117  		x := plt.X.Norm(e.XYs[i].X)
   118  		y := e.XYs[i].Y
   119  		bs = append(bs,
   120  			plot.GlyphBox{X: x, Y: plt.Y.Norm(y - err.Low), Rectangle: rect},
   121  			plot.GlyphBox{X: x, Y: plt.Y.Norm(y + err.High), Rectangle: rect})
   122  	}
   123  	return bs
   124  }
   125  
   126  // XErrorBars implements the plot.Plotter, plot.DataRanger,
   127  // and plot.GlyphBoxer interfaces, drawing horizontal error
   128  // bars, denoting error in Y values.
   129  type XErrorBars struct {
   130  	XYs
   131  
   132  	// XErrors is a copy of the X errors for each point.
   133  	XErrors
   134  
   135  	// LineStyle is the style used to draw the error bars.
   136  	draw.LineStyle
   137  
   138  	// CapWidth is the width of the caps drawn at the top
   139  	// of each error bar.
   140  	CapWidth vg.Length
   141  }
   142  
   143  // Returns a new XErrorBars plotter, or an error on failure. The error values
   144  // from the XErrorer interface are interpreted as relative to the corresponding
   145  // X value. The errors for a given X value are computed by taking the absolute
   146  // value of the error returned by the XErrorer and subtracting the first and
   147  // adding the second to the X value.
   148  func NewXErrorBars(xerrs interface {
   149  	XYer
   150  	XErrorer
   151  }) (*XErrorBars, error) {
   152  
   153  	errors := make(XErrors, xerrs.Len())
   154  	for i := range errors {
   155  		errors[i].Low, errors[i].High = xerrs.XError(i)
   156  		if err := CheckFloats(errors[i].Low, errors[i].High); err != nil {
   157  			return nil, err
   158  		}
   159  	}
   160  	xys, err := CopyXYs(xerrs)
   161  	if err != nil {
   162  		return nil, err
   163  	}
   164  
   165  	return &XErrorBars{
   166  		XYs:       xys,
   167  		XErrors:   errors,
   168  		LineStyle: DefaultLineStyle,
   169  		CapWidth:  DefaultCapWidth,
   170  	}, nil
   171  }
   172  
   173  // Plot implements the Plotter interface, drawing labels.
   174  func (e *XErrorBars) Plot(c draw.Canvas, p *plot.Plot) {
   175  	trX, trY := p.Transforms(&c)
   176  	for i, err := range e.XErrors {
   177  		y := trY(e.XYs[i].Y)
   178  		xlow := trX(e.XYs[i].X - math.Abs(err.Low))
   179  		xhigh := trX(e.XYs[i].X + math.Abs(err.High))
   180  
   181  		bar := c.ClipLinesX([]vg.Point{{X: xlow, Y: y}, {X: xhigh, Y: y}})
   182  		c.StrokeLines(e.LineStyle, bar...)
   183  		e.drawCap(&c, xlow, y)
   184  		e.drawCap(&c, xhigh, y)
   185  	}
   186  }
   187  
   188  // drawCap draws the cap if it is not clipped.
   189  func (e *XErrorBars) drawCap(c *draw.Canvas, x, y vg.Length) {
   190  	if !c.Contains(vg.Point{X: x, Y: y}) {
   191  		return
   192  	}
   193  	c.StrokeLine2(e.LineStyle, x, y-e.CapWidth/2, x, y+e.CapWidth/2)
   194  }
   195  
   196  // DataRange implements the plot.DataRanger interface.
   197  func (e *XErrorBars) DataRange() (xmin, xmax, ymin, ymax float64) {
   198  	ymin, ymax = Range(YValues{e})
   199  	xmin = math.Inf(1)
   200  	xmax = math.Inf(-1)
   201  	for i, err := range e.XErrors {
   202  		x := e.XYs[i].X
   203  		xlow := x - math.Abs(err.Low)
   204  		xhigh := x + math.Abs(err.High)
   205  		xmin = math.Min(math.Min(math.Min(xmin, x), xlow), xhigh)
   206  		xmax = math.Max(math.Max(math.Max(xmax, x), xlow), xhigh)
   207  	}
   208  	return
   209  }
   210  
   211  // GlyphBoxes implements the plot.GlyphBoxer interface.
   212  func (e *XErrorBars) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox {
   213  	rect := vg.Rectangle{
   214  		Min: vg.Point{
   215  			X: -e.LineStyle.Width / 2,
   216  			Y: -e.CapWidth / 2,
   217  		},
   218  		Max: vg.Point{
   219  			X: +e.LineStyle.Width / 2,
   220  			Y: +e.CapWidth / 2,
   221  		},
   222  	}
   223  	var bs []plot.GlyphBox
   224  	for i, err := range e.XErrors {
   225  		x := e.XYs[i].X
   226  		y := plt.Y.Norm(e.XYs[i].Y)
   227  		bs = append(bs,
   228  			plot.GlyphBox{X: plt.X.Norm(x - err.Low), Y: y, Rectangle: rect},
   229  			plot.GlyphBox{X: plt.X.Norm(x + err.High), Y: y, Rectangle: rect})
   230  	}
   231  	return bs
   232  }
   233  

View as plain text