...

Source file src/gonum.org/v1/plot/plotter/line.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  	"image/color"
     9  
    10  	"gonum.org/v1/plot"
    11  	"gonum.org/v1/plot/vg"
    12  	"gonum.org/v1/plot/vg/draw"
    13  )
    14  
    15  // StepKind specifies a form of a connection of two consecutive points.
    16  type StepKind int
    17  
    18  const (
    19  	// NoStep connects two points by simple line
    20  	NoStep StepKind = iota
    21  
    22  	// PreStep connects two points by following lines: vertical, horizontal.
    23  	PreStep
    24  
    25  	// MidStep connects two points by following lines: horizontal, vertical, horizontal.
    26  	// Vertical line is placed in the middle of the interval.
    27  	MidStep
    28  
    29  	// PostStep connects two points by following lines: horizontal, vertical.
    30  	PostStep
    31  )
    32  
    33  // Line implements the Plotter interface, drawing a line.
    34  type Line struct {
    35  	// XYs is a copy of the points for this line.
    36  	XYs
    37  
    38  	// StepStyle is the kind of the step line.
    39  	StepStyle StepKind
    40  
    41  	// LineStyle is the style of the line connecting the points.
    42  	// Use zero width to disable lines.
    43  	draw.LineStyle
    44  
    45  	// FillColor is the color to fill the area below the plot.
    46  	// Use nil to disable the filling. This is the default.
    47  	FillColor color.Color
    48  }
    49  
    50  // NewLine returns a Line that uses the default line style and
    51  // does not draw glyphs.
    52  func NewLine(xys XYer) (*Line, error) {
    53  	data, err := CopyXYs(xys)
    54  	if err != nil {
    55  		return nil, err
    56  	}
    57  	return &Line{
    58  		XYs:       data,
    59  		LineStyle: DefaultLineStyle,
    60  	}, nil
    61  }
    62  
    63  // Plot draws the Line, implementing the plot.Plotter interface.
    64  func (pts *Line) Plot(c draw.Canvas, plt *plot.Plot) {
    65  	trX, trY := plt.Transforms(&c)
    66  	ps := make([]vg.Point, len(pts.XYs))
    67  
    68  	for i, p := range pts.XYs {
    69  		ps[i].X = trX(p.X)
    70  		ps[i].Y = trY(p.Y)
    71  	}
    72  
    73  	if pts.FillColor != nil && len(ps) > 0 {
    74  		minY := trY(plt.Y.Min)
    75  		fillPoly := []vg.Point{{X: ps[0].X, Y: minY}}
    76  		switch pts.StepStyle {
    77  		case PreStep:
    78  			fillPoly = append(fillPoly, ps[1:]...)
    79  		case PostStep:
    80  			fillPoly = append(fillPoly, ps[:len(ps)-1]...)
    81  		default:
    82  			fillPoly = append(fillPoly, ps...)
    83  		}
    84  		fillPoly = append(fillPoly, vg.Point{X: ps[len(ps)-1].X, Y: minY})
    85  		fillPoly = c.ClipPolygonXY(fillPoly)
    86  		if len(fillPoly) > 0 {
    87  			c.SetColor(pts.FillColor)
    88  			var pa vg.Path
    89  			prev := fillPoly[0]
    90  			pa.Move(prev)
    91  			for _, pt := range fillPoly[1:] {
    92  				switch pts.StepStyle {
    93  				case NoStep:
    94  					pa.Line(pt)
    95  				case PreStep:
    96  					pa.Line(vg.Point{X: prev.X, Y: pt.Y})
    97  					pa.Line(pt)
    98  				case MidStep:
    99  					pa.Line(vg.Point{X: (prev.X + pt.X) / 2, Y: prev.Y})
   100  					pa.Line(vg.Point{X: (prev.X + pt.X) / 2, Y: pt.Y})
   101  					pa.Line(pt)
   102  				case PostStep:
   103  					pa.Line(vg.Point{X: pt.X, Y: prev.Y})
   104  					pa.Line(pt)
   105  				}
   106  				prev = pt
   107  			}
   108  			pa.Close()
   109  			c.Fill(pa)
   110  		}
   111  	}
   112  
   113  	lines := c.ClipLinesXY(ps)
   114  	if pts.LineStyle.Width != 0 && len(lines) != 0 {
   115  		c.SetLineStyle(pts.LineStyle)
   116  		for _, l := range lines {
   117  			if len(l) == 0 {
   118  				continue
   119  			}
   120  			var p vg.Path
   121  			prev := l[0]
   122  			p.Move(prev)
   123  			for _, pt := range l[1:] {
   124  				switch pts.StepStyle {
   125  				case PreStep:
   126  					p.Line(vg.Point{X: prev.X, Y: pt.Y})
   127  				case MidStep:
   128  					p.Line(vg.Point{X: (prev.X + pt.X) / 2, Y: prev.Y})
   129  					p.Line(vg.Point{X: (prev.X + pt.X) / 2, Y: pt.Y})
   130  				case PostStep:
   131  					p.Line(vg.Point{X: pt.X, Y: prev.Y})
   132  				}
   133  				p.Line(pt)
   134  				prev = pt
   135  			}
   136  			c.Stroke(p)
   137  		}
   138  	}
   139  }
   140  
   141  // DataRange returns the minimum and maximum
   142  // x and y values, implementing the plot.DataRanger interface.
   143  func (pts *Line) DataRange() (xmin, xmax, ymin, ymax float64) {
   144  	return XYRange(pts)
   145  }
   146  
   147  // GlyphBoxes implements the plot.GlyphBoxer interface.
   148  func (pts *Line) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox {
   149  	r := 0.5 * pts.LineStyle.Width
   150  	rect := vg.Rectangle{
   151  		Min: vg.Point{
   152  			X: -r,
   153  			Y: -r,
   154  		},
   155  		Max: vg.Point{
   156  			X: +r,
   157  			Y: +r,
   158  		},
   159  	}
   160  
   161  	bs := make([]plot.GlyphBox, pts.XYs.Len())
   162  	for i := range bs {
   163  		x, y := pts.XY(i)
   164  		bs[i] = plot.GlyphBox{
   165  			X:         plt.X.Norm(x),
   166  			Y:         plt.Y.Norm(y),
   167  			Rectangle: rect,
   168  		}
   169  	}
   170  	return bs
   171  }
   172  
   173  // Thumbnail returns the thumbnail for the Line, implementing the plot.Thumbnailer interface.
   174  func (pts *Line) Thumbnail(c *draw.Canvas) {
   175  	if pts.FillColor != nil {
   176  		var topY vg.Length
   177  		if pts.LineStyle.Width == 0 {
   178  			topY = c.Max.Y
   179  		} else {
   180  			topY = (c.Min.Y + c.Max.Y) / 2
   181  		}
   182  		points := []vg.Point{
   183  			{X: c.Min.X, Y: c.Min.Y},
   184  			{X: c.Min.X, Y: topY},
   185  			{X: c.Max.X, Y: topY},
   186  			{X: c.Max.X, Y: c.Min.Y},
   187  		}
   188  		poly := c.ClipPolygonY(points)
   189  		c.FillPolygon(pts.FillColor, poly)
   190  	}
   191  
   192  	if pts.LineStyle.Width != 0 {
   193  		y := c.Center().Y
   194  		c.StrokeLine2(pts.LineStyle, c.Min.X, y, c.Max.X, y)
   195  	}
   196  }
   197  
   198  // NewLinePoints returns both a Line and a
   199  // Points for the given point data.
   200  func NewLinePoints(xys XYer) (*Line, *Scatter, error) {
   201  	s, err := NewScatter(xys)
   202  	if err != nil {
   203  		return nil, nil, err
   204  	}
   205  	l := &Line{
   206  		XYs:       s.XYs,
   207  		LineStyle: DefaultLineStyle,
   208  	}
   209  	return l, s, nil
   210  }
   211  

View as plain text