Skip to content
Closed
32 changes: 16 additions & 16 deletions axis.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,9 @@ func makeAxis() (Axis, error) {
return a, nil
}

// sanitizeRange ensures that the range of the
// SanitizeRange ensures that the range of the
// axis makes sense.
func (a *Axis) sanitizeRange() {
func (a *Axis) SanitizeRange() {
if math.IsInf(a.Min, 0) {
a.Min = 0
}
Expand Down Expand Up @@ -177,14 +177,14 @@ func (a *Axis) drawTicks() bool {
return a.Tick.Width > 0 && a.Tick.Length > 0
}

// A horizontalAxis draws horizontally across the bottom
// A HorizontalAxis draws horizontally across the bottom
// of a plot.
type horizontalAxis struct {
type HorizontalAxis struct {
Axis
}

// size returns the height of the axis.
func (a *horizontalAxis) size() (h vg.Length) {
// Size returns the height of the axis.
func (a *HorizontalAxis) Size() (h vg.Length) {
if a.Label.Text != "" {
h -= a.Label.Font.Extents().Descent
h += a.Label.Height(a.Label.Text)
Expand All @@ -200,8 +200,8 @@ func (a *horizontalAxis) size() (h vg.Length) {
return
}

// draw draws the axis along the lower edge of a draw.Canvas.
func (a *horizontalAxis) draw(c draw.Canvas) {
// Draw draws the axis along the lower edge of a draw.Canvas.
func (a *HorizontalAxis) Draw(c draw.Canvas) {
y := c.Min.Y
if a.Label.Text != "" {
y -= a.Label.Font.Extents().Descent
Expand Down Expand Up @@ -241,7 +241,7 @@ func (a *horizontalAxis) draw(c draw.Canvas) {
}

// GlyphBoxes returns the GlyphBoxes for the tick labels.
func (a *horizontalAxis) GlyphBoxes(*Plot) (boxes []GlyphBox) {
func (a *HorizontalAxis) GlyphBoxes(*Plot) (boxes []GlyphBox) {
for _, t := range a.Tick.Marker.Ticks(a.Min, a.Max) {
if t.IsMinor() {
continue
Expand All @@ -259,13 +259,13 @@ func (a *horizontalAxis) GlyphBoxes(*Plot) (boxes []GlyphBox) {
return
}

// A verticalAxis is drawn vertically up the left side of a plot.
type verticalAxis struct {
// A VerticalAxis is drawn vertically up the left side of a plot.
type VerticalAxis struct {
Axis
}

// size returns the width of the axis.
func (a *verticalAxis) size() (w vg.Length) {
// Size returns the width of the axis.
func (a *VerticalAxis) Size() (w vg.Length) {
if a.Label.Text != "" {
w -= a.Label.Font.Extents().Descent
w += a.Label.Height(a.Label.Text)
Expand All @@ -284,8 +284,8 @@ func (a *verticalAxis) size() (w vg.Length) {
return
}

// draw draws the axis along the left side of a draw.Canvas.
func (a *verticalAxis) draw(c draw.Canvas) {
// Draw draws the axis along the left side of a draw.Canvas.
func (a *VerticalAxis) Draw(c draw.Canvas) {
x := c.Min.X
if a.Label.Text != "" {
x += a.Label.Height(a.Label.Text)
Expand Down Expand Up @@ -327,7 +327,7 @@ func (a *verticalAxis) draw(c draw.Canvas) {
}

// GlyphBoxes returns the GlyphBoxes for the tick labels
func (a *verticalAxis) GlyphBoxes(*Plot) (boxes []GlyphBox) {
func (a *VerticalAxis) GlyphBoxes(*Plot) (boxes []GlyphBox) {
for _, t := range a.Tick.Marker.Ticks(a.Min, a.Max) {
if t.IsMinor() {
continue
Expand Down
12 changes: 0 additions & 12 deletions export_test.go

This file was deleted.

2 changes: 2 additions & 0 deletions gob/gob.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,6 @@ func init() {
gob.Register(plotter.XYZs{})
gob.Register(plotter.XYValues{})

// plot.Drawer
gob.Register(plot.DefaultPlotStyle{})
}
4 changes: 2 additions & 2 deletions legend.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ func makeLegend() (Legend, error) {
}, nil
}

// draw draws the legend to the given draw.Canvas.
func (l *Legend) draw(c draw.Canvas) {
// Draw draws the legend to the given draw.Canvas.
func (l *Legend) Draw(c draw.Canvas) {
iconx := c.Min.X
textx := iconx + l.ThumbnailWidth + l.TextStyle.Width(" ")
xalign := 0.0
Expand Down
102 changes: 65 additions & 37 deletions plot.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ type Plot struct {
// Legend is the plot's legend.
Legend Legend

// Style draws the plot with style
Style PlotDrawer

// plotters are drawn by calling their Plot method
// after the axes are drawn.
plotters []Plotter
Expand All @@ -74,6 +77,11 @@ type Plotter interface {
Plot(draw.Canvas, *Plot)
}

// PlotDrawer draws a Plot on a draw.Canvas
type PlotDrawer interface {
DrawPlot(p *Plot, c draw.Canvas)
}

// DataRanger wraps the DataRange method.
type DataRanger interface {
// DataRange returns the range of X and Y values.
Expand Down Expand Up @@ -104,6 +112,7 @@ func New() (*Plot, error) {
X: x,
Y: y,
Legend: legend,
Style: DefaultPlotStyle{},
}
p.Title.TextStyle = draw.TextStyle{
Color: color.Black,
Expand All @@ -112,6 +121,11 @@ func New() (*Plot, error) {
return p, nil
}

// Plotters returns the list of plotters attached to this Plot.
func (p *Plot) Plotters() []Plotter {
return p.plotters
}

// Add adds a Plotters to the plot.
//
// If the plotters implements DataRanger then the
Expand Down Expand Up @@ -143,32 +157,7 @@ func (p *Plot) Add(ps ...Plotter) {
// taken into account when padding the plot so that
// none of their glyphs are clipped.
func (p *Plot) Draw(c draw.Canvas) {
if p.BackgroundColor != nil {
c.SetColor(p.BackgroundColor)
c.Fill(c.Rectangle.Path())
}
if p.Title.Text != "" {
c.FillText(p.Title.TextStyle, vg.Point{c.Center().X, c.Max.Y}, -0.5, -1, p.Title.Text)
c.Max.Y -= p.Title.Height(p.Title.Text) - p.Title.Font.Extents().Descent
c.Max.Y -= p.Title.Padding
}

p.X.sanitizeRange()
x := horizontalAxis{p.X}
p.Y.sanitizeRange()
y := verticalAxis{p.Y}

ywidth := y.size()
x.draw(padX(p, draw.Crop(c, ywidth, 0, 0, 0)))
xheight := x.size()
y.draw(padY(p, draw.Crop(c, 0, 0, xheight, 0)))

dataC := padY(p, padX(p, draw.Crop(c, ywidth, 0, xheight, 0)))
for _, data := range p.plotters {
data.Plot(dataC, p)
}

p.Legend.draw(draw.Crop(draw.Crop(c, ywidth, 0, 0, 0), 0, 0, xheight, 0))
p.Style.DrawPlot(p, c)
}

// DataCanvas returns a new draw.Canvas that
Expand All @@ -179,11 +168,11 @@ func (p *Plot) DataCanvas(da draw.Canvas) draw.Canvas {
da.Max.Y -= p.Title.Height(p.Title.Text) - p.Title.Font.Extents().Descent
da.Max.Y -= p.Title.Padding
}
p.X.sanitizeRange()
x := horizontalAxis{p.X}
p.Y.sanitizeRange()
y := verticalAxis{p.Y}
return padY(p, padX(p, draw.Crop(da, y.size(), x.size(), 0, 0)))
p.X.SanitizeRange()
x := HorizontalAxis{p.X}
p.Y.SanitizeRange()
y := VerticalAxis{p.Y}
return PadY(p, PadX(p, draw.Crop(da, y.Size(), x.Size(), 0, 0)))
}

// DrawGlyphBoxes draws red outlines around the plot's
Expand All @@ -197,12 +186,51 @@ func (p *Plot) DrawGlyphBoxes(c *draw.Canvas) {
}
}

// padX returns a draw.Canvas that is padded horizontally
// DefaultPlotStyle implements PlotDrawer
type DefaultPlotStyle struct{}

// DrawPlot draws a plot to a draw.Canvas.
//
// Plotters are drawn in the order in which they were
// added to the plot. Plotters that implement the
// GlyphBoxer interface will have their GlyphBoxes
// taken into account when padding the plot so that
// none of their glyphs are clipped.
func (DefaultPlotStyle) DrawPlot(p *Plot, c draw.Canvas) {
if p.BackgroundColor != nil {
c.SetColor(p.BackgroundColor)
c.Fill(c.Rectangle.Path())
}
if p.Title.Text != "" {
c.FillText(p.Title.TextStyle, vg.Point{c.Center().X, c.Max.Y}, -0.5, -1, p.Title.Text)
c.Max.Y -= p.Title.Height(p.Title.Text) - p.Title.Font.Extents().Descent
c.Max.Y -= p.Title.Padding
}

p.X.SanitizeRange()
x := HorizontalAxis{p.X}
p.Y.SanitizeRange()
y := VerticalAxis{p.Y}

ywidth := y.Size()
x.Draw(PadX(p, draw.Crop(c, ywidth, 0, 0, 0)))
xheight := x.Size()
y.Draw(PadY(p, draw.Crop(c, 0, 0, xheight, 0)))

dataC := PadY(p, PadX(p, draw.Crop(c, ywidth, 0, xheight, 0)))
for _, data := range p.plotters {
data.Plot(dataC, p)
}

p.Legend.Draw(draw.Crop(draw.Crop(c, ywidth, 0, 0, 0), 0, 0, xheight, 0))
}

// PadX returns a draw.Canvas that is padded horizontally
// so that glyphs will no be clipped.
func padX(p *Plot, c draw.Canvas) draw.Canvas {
func PadX(p *Plot, c draw.Canvas) draw.Canvas {
glyphs := p.GlyphBoxes(p)
l := leftMost(&c, glyphs)
xAxis := horizontalAxis{p.X}
xAxis := HorizontalAxis{p.X}
glyphs = append(glyphs, xAxis.GlyphBoxes(p)...)
r := rightMost(&c, glyphs)

Expand Down Expand Up @@ -253,12 +281,12 @@ func leftMost(c *draw.Canvas, boxes []GlyphBox) GlyphBox {
return l
}

// padY returns a draw.Canvas that is padded vertically
// PadY returns a draw.Canvas that is padded vertically
// so that glyphs will no be clipped.
func padY(p *Plot, c draw.Canvas) draw.Canvas {
func PadY(p *Plot, c draw.Canvas) draw.Canvas {
glyphs := p.GlyphBoxes(p)
b := bottomMost(&c, glyphs)
yAxis := verticalAxis{p.Y}
yAxis := VerticalAxis{p.Y}
glyphs = append(glyphs, yAxis.GlyphBoxes(p)...)
t := topMost(&c, glyphs)

Expand Down
Loading