go-hep.org/x/hep@v0.38.1/hplot/functions.go (about)

     1  // Copyright ©2019 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  // Copyright ©2015 The Gonum Authors. All rights reserved.
     6  // Use of this source code is governed by a BSD-style
     7  // license that can be found in the LICENSE file.
     8  
     9  package hplot
    10  
    11  import (
    12  	"math"
    13  
    14  	"gonum.org/v1/plot"
    15  	"gonum.org/v1/plot/plotter"
    16  	"gonum.org/v1/plot/vg"
    17  	"gonum.org/v1/plot/vg/draw"
    18  )
    19  
    20  // Function implements the Plotter interface,
    21  // drawing a line for the given function.
    22  type Function struct {
    23  	F func(x float64) (y float64)
    24  
    25  	// XMin and XMax specify the range
    26  	// of x values to pass to F.
    27  	XMin, XMax float64
    28  
    29  	Samples int
    30  
    31  	draw.LineStyle
    32  
    33  	// LogY allows rendering with a log-scaled Y axis.
    34  	// When enabled, function values returning 0 will be discarded from
    35  	// the final plot.
    36  	LogY bool
    37  }
    38  
    39  // NewFunction returns a Function that plots F using
    40  // the default line style with 50 samples.
    41  func NewFunction(f func(float64) float64) *Function {
    42  	return &Function{
    43  		F:         f,
    44  		Samples:   50,
    45  		LineStyle: plotter.DefaultLineStyle,
    46  	}
    47  }
    48  
    49  // Plot implements the Plotter interface, drawing a line
    50  // that connects each point in the Line.
    51  func (f *Function) Plot(c draw.Canvas, p *plot.Plot) {
    52  	trX, trY := p.Transforms(&c)
    53  
    54  	min, max := f.XMin, f.XMax
    55  	if min == 0 && max == 0 {
    56  		min = p.X.Min
    57  		max = p.X.Max
    58  	}
    59  	d := (max - min) / float64(f.Samples-1)
    60  	switch {
    61  	case f.LogY:
    62  		var (
    63  			line  = 0
    64  			lines = [][]vg.Point{make([]vg.Point, 0, f.Samples)}
    65  		)
    66  		for i := range f.Samples {
    67  			x := min + float64(i)*d
    68  			y := f.F(x)
    69  			switch {
    70  			case math.IsInf(y, -1) || y <= 0:
    71  				line++
    72  				lines = append(lines, make([]vg.Point, 0, f.Samples-i))
    73  			default:
    74  				lines[line] = append(lines[line], vg.Point{
    75  					X: trX(x),
    76  					Y: trY(y),
    77  				})
    78  			}
    79  		}
    80  		for _, line := range lines {
    81  			if len(line) <= 1 {
    82  				// FIXME(sbinet): we should find a couple of points around...
    83  				continue
    84  			}
    85  			c.StrokeLines(f.LineStyle, c.ClipLinesXY(line)...)
    86  		}
    87  	default:
    88  		line := make([]vg.Point, f.Samples)
    89  		for i := range line {
    90  			x := min + float64(i)*d
    91  			y := f.F(x)
    92  			line[i].X = trX(x)
    93  			line[i].Y = trY(y)
    94  		}
    95  		c.StrokeLines(f.LineStyle, c.ClipLinesXY(line)...)
    96  	}
    97  }
    98  
    99  // Thumbnail draws a line in the given style down the
   100  // center of a DrawArea as a thumbnail representation
   101  // of the LineStyle of the function.
   102  func (f Function) Thumbnail(c *draw.Canvas) {
   103  	y := c.Center().Y
   104  	c.StrokeLine2(f.LineStyle, c.Min.X, y, c.Max.X, y)
   105  }