// DrawGlyphBoxes draws red outlines around the plot's // GlyphBoxes. This is intended for debugging. func (p *Plot) DrawGlyphBoxes(c *draw.Canvas) { c.SetColor(color.RGBA{R: 255, A: 255}) for _, b := range p.GlyphBoxes(p) { b.Rectangle.Min.X += c.X(b.X) b.Rectangle.Min.Y += c.Y(b.Y) c.Stroke(b.Rectangle.Path()) } }
// Plot draws the Line, implementing the plot.Plotter interface. func (rp *ResponsePlotter) Plot(canvas vgdraw.Canvas, plt *plot.Plot) { trX, trY := plt.Transforms(&canvas) start := float64(rp.Response.GetStartTime()) step := float64(rp.Response.GetStepTime()) absent := rp.Response.IsAbsent lines := make([][]vgdraw.Point, 1) lines[0] = make([]vgdraw.Point, 0, len(rp.Response.Values)) /* ikruglov * swithing between lineMode and looping inside * is more branch-prediction friendly i.e. potentially faster */ switch rp.lineMode { case "slope": currentLine := 0 lastAbsent := false for i, v := range rp.Response.Values { if absent[i] { lastAbsent = true } else if lastAbsent { currentLine++ lines = append(lines, make([]vgdraw.Point, 1)) lines[currentLine][0] = vgdraw.Point{X: trX(start + float64(i)*step), Y: trY(v)} lastAbsent = false } else { lines[currentLine] = append(lines[currentLine], vgdraw.Point{X: trX(start + float64(i)*step), Y: trY(v)}) } } case "connected": for i, v := range rp.Response.Values { if absent[i] { continue } lines[0] = append(lines[0], vgdraw.Point{X: trX(start + float64(i)*step), Y: trY(v)}) } case "drawAsInfinite": for i, v := range rp.Response.Values { if !absent[i] && v > 0 { infiniteLine := []vgdraw.Point{ vgdraw.Point{X: trX(start + float64(i)*step), Y: canvas.Y(1)}, vgdraw.Point{X: trX(start + float64(i)*step), Y: canvas.Y(0)}, } lines = append(lines, infiniteLine) } } //case "staircase": // TODO default: panic("Unimplemented " + rp.lineMode) } canvas.StrokeLines(rp.LineStyle, lines...) }
func (g GlyphBoxes) Plot(c draw.Canvas, plt *plot.Plot) { for _, b := range plt.GlyphBoxes(plt) { x := c.X(b.X) + b.Rectangle.Min.X y := c.Y(b.Y) + b.Rectangle.Min.Y c.StrokeLines(g.LineStyle, []draw.Point{ {x, y}, {x + b.Rectangle.Size().X, y}, {x + b.Rectangle.Size().X, y + b.Rectangle.Size().Y}, {x, y + b.Rectangle.Size().Y}, {x, y}, }) } }
// bottomMost returns the bottom-most GlyphBox. func bottomMost(c *draw.Canvas, boxes []GlyphBox) GlyphBox { miny := c.Min.Y l := GlyphBox{} for _, b := range boxes { if b.Size().Y <= 0 { continue } if y := c.Y(b.Y) + b.Min.Y; y < miny && b.Y >= 0 { miny = y l = b } } return l }
// topMost returns the top-most GlyphBox. func topMost(c *draw.Canvas, boxes []GlyphBox) GlyphBox { maxy := c.Max.Y t := GlyphBox{Y: 1} for _, b := range boxes { if b.Size().Y <= 0 { continue } if y := c.Y(b.Y) + b.Min.Y + b.Size().Y; y > maxy && b.Y <= 1 { maxy = y t = b } } return t }
// 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) }
// Transforms returns functions to transfrom // from the x and y data coordinate system to // the draw coordinate system of the given // draw area. func (p *Plot) Transforms(c *draw.Canvas) (x, y func(float64) vg.Length) { x = func(x float64) vg.Length { return c.X(p.X.Norm(x)) } y = func(y float64) vg.Length { return c.Y(p.Y.Norm(y)) } return }