-
Notifications
You must be signed in to change notification settings - Fork 204
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
267 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
// Copyright ©2015 The gonum Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package plotter | ||
|
||
import ( | ||
"image/color" | ||
"math" | ||
|
||
"gonum.org/v1/plot" | ||
"gonum.org/v1/plot/vg" | ||
"gonum.org/v1/plot/vg/draw" | ||
) | ||
|
||
// StepType specifies where to put a vertical line joining two steps | ||
type StepType int | ||
|
||
const ( | ||
// StepTypePre means that a vertical line is placed at the begining of a horizonal line (|‾‾) | ||
StepTypePre StepType = iota | ||
|
||
// StepTypeMid means that a vertical line is placed between two horizontal lines (_|‾) | ||
StepTypeMid | ||
|
||
// StepTypePost means that a vertial line is placed at the end of a horizonal line (__|) | ||
StepTypePost | ||
) | ||
|
||
// Step implements the Plotter interface, drawing a stepped line. | ||
type Step struct { | ||
// XYs is a copy of the points for this line. | ||
XYs | ||
|
||
// StepStyle is the type of step line | ||
StepType StepType | ||
|
||
// LineStyle is the style of the line connecting the points. | ||
LineStyle *draw.LineStyle | ||
|
||
// ShadeColor is the color of the shaded area. | ||
ShadeColor color.Color | ||
} | ||
|
||
// NewStep returns a Step that uses the default line style and does not draw glyphs. | ||
func NewStep(xys XYer) (*Step, error) { | ||
data, err := CopyXYs(xys) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return &Step{ | ||
XYs: data, | ||
LineStyle: &DefaultLineStyle, | ||
}, nil | ||
} | ||
|
||
// Plot draws the Step, implementing the plot.Plotter interface. | ||
func (pts *Step) Plot(c draw.Canvas, plt *plot.Plot) { | ||
trX, trY := plt.Transforms(&c) | ||
ps := make([]vg.Point, len(pts.XYs)) | ||
|
||
for i, p := range pts.XYs { | ||
ps[i].X = trX(p.X) | ||
ps[i].Y = trY(p.Y) | ||
} | ||
|
||
if pts.ShadeColor != nil && len(ps) > 0 { | ||
c.SetColor(pts.ShadeColor) | ||
minY := trY(plt.Y.Min) | ||
var pa vg.Path | ||
pa.Move(vg.Point{X: ps[0].X, Y: minY}) | ||
prev := ps[0] | ||
if pts.StepType != StepTypePre { | ||
pa.Line(prev) | ||
} | ||
for _, pt := range ps[1:] { | ||
switch pts.StepType { | ||
case StepTypePre: | ||
pa.Line(vg.Point{X: prev.X, Y: pt.Y}) | ||
case StepTypeMid: | ||
pa.Line(vg.Point{X: (prev.X + pt.X) / 2, Y: prev.Y}) | ||
pa.Line(vg.Point{X: (prev.X + pt.X) / 2, Y: pt.Y}) | ||
case StepTypePost: | ||
pa.Line(vg.Point{X: pt.X, Y: prev.Y}) | ||
} | ||
pa.Line(pt) | ||
prev = pt | ||
} | ||
pa.Line(vg.Point{X: ps[len(pts.XYs)-1].X, Y: minY}) | ||
pa.Close() | ||
c.Fill(pa) | ||
} | ||
|
||
if pts.LineStyle != nil { | ||
lines := c.ClipLinesXY(ps) | ||
if len(lines) == 0 { | ||
return | ||
} | ||
c.SetLineStyle(*pts.LineStyle) | ||
for _, l := range lines { | ||
if len(l) == 0 { | ||
continue | ||
} | ||
var p vg.Path | ||
prev := l[0] | ||
p.Move(prev) | ||
for _, pt := range l[1:] { | ||
switch pts.StepType { | ||
case StepTypePre: | ||
p.Line(vg.Point{X: prev.X, Y: pt.Y}) | ||
case StepTypeMid: | ||
p.Line(vg.Point{X: (prev.X + pt.X) / 2, Y: prev.Y}) | ||
p.Line(vg.Point{X: (prev.X + pt.X) / 2, Y: pt.Y}) | ||
case StepTypePost: | ||
p.Line(vg.Point{X: pt.X, Y: prev.Y}) | ||
} | ||
p.Line(pt) | ||
prev = pt | ||
} | ||
c.Stroke(p) | ||
} | ||
} | ||
} | ||
|
||
// DataRange returns the minimum and maximum | ||
// x and y values, implementing the plot.DataRanger | ||
// interface. | ||
func (pts *Step) DataRange() (xmin, xmax, ymin, ymax float64) { | ||
if pts.ShadeColor != nil { | ||
xmin, xmax, ymin, ymax = XYRange(pts) | ||
ymin = math.Min(ymin, 0.) | ||
ymax = math.Max(ymax, 0.) | ||
return | ||
} | ||
return XYRange(pts) | ||
} | ||
|
||
// Thumbnail the thumbnail for the Step, | ||
// implementing the plot.Thumbnailer interface. | ||
func (pts *Step) Thumbnail(c *draw.Canvas) { | ||
if pts.ShadeColor != nil { | ||
points := []vg.Point{ | ||
{X: c.Min.X, Y: c.Min.Y}, | ||
{X: c.Min.X, Y: c.Max.Y}, | ||
{X: c.Max.X, Y: c.Max.Y}, | ||
{X: c.Max.X, Y: c.Min.Y}, | ||
} | ||
poly := c.ClipPolygonY(points) | ||
c.FillPolygon(pts.ShadeColor, poly) | ||
} else if pts.LineStyle != nil { | ||
y := c.Center().Y | ||
c.StrokeLine2(*pts.LineStyle, c.Min.X, y, c.Max.X, y) | ||
} | ||
} | ||
|
||
// NewStepPoints returns both a Step and a | ||
// Points for the given point data. | ||
func NewStepPoints(xys XYer) (*Step, *Scatter, error) { | ||
s, err := NewScatter(xys) | ||
if err != nil { | ||
return nil, nil, err | ||
} | ||
l := &Step{ | ||
XYs: s.XYs, | ||
LineStyle: &DefaultLineStyle, | ||
} | ||
return l, s, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
// Copyright ©2015 The gonum Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package plotter | ||
|
||
import ( | ||
"image/color" | ||
"log" | ||
"testing" | ||
|
||
"golang.org/x/exp/rand" | ||
|
||
"gonum.org/v1/plot" | ||
"gonum.org/v1/plot/internal/cmpimg" | ||
"gonum.org/v1/plot/vg" | ||
"gonum.org/v1/plot/vg/draw" | ||
) | ||
|
||
// ExampleStep draws some filled step lines. | ||
func ExampleStep() { | ||
rnd := rand.New(rand.NewSource(1)) | ||
|
||
// randomPoints returns some random x, y points | ||
// with some interesting kind of trend. | ||
randomPoints := func(n int, x float64) XYs { | ||
pts := make(XYs, n) | ||
for i := range pts { | ||
if i == 0 { | ||
pts[i].X = x + rnd.Float64() | ||
} else { | ||
pts[i].X = pts[i-1].X + 0.5 + rnd.Float64() | ||
} | ||
pts[i].Y = 5. + 10*rnd.Float64() | ||
} | ||
return pts | ||
} | ||
|
||
n := 4 | ||
|
||
p, err := plot.New() | ||
if err != nil { | ||
log.Panic(err) | ||
} | ||
p.Title.Text = "Step Example" | ||
p.X.Label.Text = "X" | ||
p.Y.Label.Text = "Y" | ||
p.Add(NewGrid()) | ||
|
||
stepPre, err := NewStep(randomPoints(n, 0.)) | ||
if err != nil { | ||
log.Panic(err) | ||
} | ||
stepPre.ShadeColor = color.RGBA{A: 40} | ||
|
||
stepMid, err := NewStep(randomPoints(n, 3.5)) | ||
if err != nil { | ||
log.Panic(err) | ||
} | ||
stepMid.StepType = StepTypeMid | ||
stepMid.LineStyle = &draw.LineStyle{Color: color.RGBA{R: 196, B: 128, A: 255}, Width: vg.Points(1)} | ||
|
||
stepPost, err := NewStep(randomPoints(n, 7.)) | ||
if err != nil { | ||
log.Panic(err) | ||
} | ||
stepPost.StepType = StepTypePost | ||
stepPost.LineStyle = &draw.LineStyle{Color: color.RGBA{B: 255, A: 255}, Width: vg.Points(1)} | ||
stepPost.ShadeColor = color.RGBA{B: 255, A: 40} | ||
|
||
scPre, err := NewScatter(stepPre.XYs) | ||
if err != nil { | ||
log.Panic(err) | ||
} | ||
|
||
scMid, err := NewScatter(stepMid.XYs) | ||
if err != nil { | ||
log.Panic(err) | ||
} | ||
|
||
scPost, err := NewScatter(stepPost.XYs) | ||
if err != nil { | ||
log.Panic(err) | ||
} | ||
|
||
p.Add(stepPre, stepMid, stepPost, scPre, scMid, scPost) | ||
p.Legend.Add("pre", stepPre) | ||
p.Legend.Add("mid", stepMid) | ||
p.Legend.Add("post", stepPost) | ||
|
||
err = p.Save(200, 200, "testdata/step.pdf") | ||
if err != nil { | ||
log.Panic(err) | ||
} | ||
} | ||
|
||
func TestStep(t *testing.T) { | ||
cmpimg.CheckPlot(ExampleStep, t, "step.pdf") | ||
} |
Binary file not shown.