// Draw 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 (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, 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, c.Crop(ywidth, 0, 0, 0))) xheight := x.size() y.draw(padY(p, c.Crop(0, xheight, 0, 0))) dataC := padY(p, padX(p, c.Crop(ywidth, xheight, 0, 0))) for _, data := range p.plotters { data.Plot(dataC, p) } p.Legend.draw(c.Crop(ywidth, 0, 0, 0).Crop(0, xheight, 0, 0)) }
// Plot implements the Plotter interface, drawing labels. func (l *Labels) Plot(c draw.Canvas, p *plot.Plot) { trX, trY := p.Transforms(&c) for i, label := range l.Labels { x := trX(l.XYs[i].X) y := trY(l.XYs[i].Y) if !c.Contains(draw.Point{x, y}) { continue } x += l.XOffset y += l.YOffset c.FillText(l.TextStyle, x, y, l.XAlign, l.YAlign, label) } }
// 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) c.Push() c.Rotate(math.Pi / 2) c.FillText(a.Label.TextStyle, c.Center().Y, -x, -0.5, 0, a.Label.Text) c.Pop() x += -a.Label.Font.Extents().Descent } marks := a.Tick.Marker.Ticks(a.Min, a.Max) if w := tickLabelWidth(a.Tick.Label, marks); len(marks) > 0 && w > 0 { x += w } major := false for _, t := range marks { y := c.Y(a.Norm(t.Value)) if !c.ContainsY(y) || t.IsMinor() { continue } c.FillText(a.Tick.Label, x, y, -1, -0.5, t.Label) major = true } if major { x += a.Tick.Label.Width(" ") } if a.drawTicks() && len(marks) > 0 { len := a.Tick.Length for _, t := range marks { y := c.Y(a.Norm(t.Value)) if !c.ContainsY(y) { continue } start := t.lengthOffset(len) c.StrokeLine2(a.Tick.LineStyle, x+start, y, x+len, y) } x += len } c.StrokeLine2(a.LineStyle, x, c.Min.Y, x, c.Max.Y) }
// 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 c.FillText(a.Label.TextStyle, c.Center().X, y, -0.5, 0, a.Label.Text) y += a.Label.Height(a.Label.Text) } marks := a.Tick.Marker.Ticks(a.Min, a.Max) for _, t := range marks { x := c.X(a.Norm(t.Value)) if !c.ContainsX(x) || t.IsMinor() { continue } c.FillText(a.Tick.Label, x, y, -0.5, 0, t.Label) } if len(marks) > 0 { y += tickLabelHeight(a.Tick.Label, marks) } else { y += a.Width / 2 } if len(marks) > 0 && a.drawTicks() { len := a.Tick.Length for _, t := range marks { x := c.X(a.Norm(t.Value)) if !c.ContainsX(x) { continue } start := t.lengthOffset(len) c.StrokeLine2(a.Tick.LineStyle, x, y+start, x, y+len) } y += len } c.StrokeLine2(a.LineStyle, c.Min.X, y, c.Max.X, y) }
// 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 if !l.Left { iconx = c.Max.X - l.ThumbnailWidth textx = iconx - l.TextStyle.Width(" ") xalign = -1 } textx += l.XOffs iconx += l.XOffs enth := l.entryHeight() y := c.Max.Y - enth if !l.Top { y = c.Min.Y + (enth+l.Padding)*(vg.Length(len(l.entries))-1) } y += l.YOffs icon := &draw.Canvas{ Canvas: c.Canvas, Rectangle: draw.Rectangle{ Min: draw.Point{iconx, y}, Max: draw.Point{iconx + l.ThumbnailWidth, y + enth}, }, } for _, e := range l.entries { for _, t := range e.thumbs { t.Thumbnail(icon) } yoffs := (enth - l.TextStyle.Height(e.text)) / 2 c.FillText(l.TextStyle, textx, icon.Min.Y+yoffs, xalign, 0, e.text) icon.Min.Y -= enth + l.Padding icon.Max.Y -= enth + l.Padding } }