Skip to content

Commit

Permalink
plot: properly handle descent in axis labels glyph boxes
Browse files Browse the repository at this point in the history
Fixes gonum#676.

Signed-off-by: Sebastien Binet <binet@cern.ch>
  • Loading branch information
sbinet committed Aug 4, 2021
1 parent 701554e commit fc1c0f0
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 12 deletions.
49 changes: 43 additions & 6 deletions axis.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,14 +296,29 @@ func (a horizontalAxis) draw(c draw.Canvas) {

// GlyphBoxes returns the GlyphBoxes for the tick labels.
func (a horizontalAxis) GlyphBoxes(*Plot) []GlyphBox {
var boxes []GlyphBox
for _, t := range a.Tick.Marker.Ticks(a.Min, a.Max) {
var (
boxes []GlyphBox
yoff font.Length
)

if a.Label.Text != "" {
// yoff += a.Label.TextStyle.FontExtents().Descent
yoff += a.Label.TextStyle.Height(a.Label.Text)
yoff += a.Label.Padding
}

var (
marks = a.Tick.Marker.Ticks(a.Min, a.Max)
height = tickLabelHeight(a.Tick.Label, marks)
descent = a.Tick.Label.FontExtents().Descent
)
for _, t := range marks {
if t.IsMinor() {
continue
}
box := GlyphBox{
X: a.Norm(t.Value),
Rectangle: a.Tick.Label.Rectangle(t.Label),
Rectangle: a.Tick.Label.Rectangle(t.Label).Add(vg.Point{Y: yoff + height + descent}),
}
boxes = append(boxes, box)
}
Expand Down Expand Up @@ -397,14 +412,36 @@ func (a verticalAxis) draw(c draw.Canvas) {

// GlyphBoxes returns the GlyphBoxes for the tick labels
func (a verticalAxis) GlyphBoxes(*Plot) []GlyphBox {
var boxes []GlyphBox
for _, t := range a.Tick.Marker.Ticks(a.Min, a.Max) {
var (
boxes []GlyphBox
xoff font.Length
)

if a.Label.Text != "" {
sty := a.Label.TextStyle
sty.Rotation += math.Pi / 2

xoff += a.Label.TextStyle.Height(a.Label.Text)
xoff += a.Label.TextStyle.FontExtents().Descent
xoff += a.Label.Padding
}

marks := a.Tick.Marker.Ticks(a.Min, a.Max)
if w := tickLabelWidth(a.Tick.Label, marks); len(marks) > 0 && w > 0 {
xoff += w
}

var (
ext = a.Tick.Label.FontExtents()
desc = ext.Height - ext.Ascent
)
for _, t := range marks {
if t.IsMinor() {
continue
}
box := GlyphBox{
Y: a.Norm(t.Value),
Rectangle: a.Tick.Label.Rectangle(t.Label),
Rectangle: a.Tick.Label.Rectangle(t.Label).Add(vg.Point{X: xoff, Y: desc}),
}
boxes = append(boxes, box)
}
Expand Down
6 changes: 5 additions & 1 deletion legend.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,8 +173,12 @@ func (l *Legend) Rectangle(c draw.Canvas) vg.Rectangle {
// entryHeight returns the height of the tallest legend
// entry text.
func (l *Legend) entryHeight() (height vg.Length) {
var (
ext = l.TextStyle.FontExtents()
desc = ext.Height - ext.Ascent // descent + linegap
)
for _, e := range l.entries {
if h := l.TextStyle.Rectangle(e.text).Max.Y; h > height {
if h := l.TextStyle.Rectangle(e.text).Max.Y + desc; h > height {
height = h
}
}
Expand Down
44 changes: 39 additions & 5 deletions plot.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,12 +191,46 @@ func (p *Plot) DataCanvas(da draw.Canvas) draw.Canvas {

// 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})
func (p *Plot) DrawGlyphBoxes(c draw.Canvas) {
dac := p.DataCanvas(c)
sty := draw.LineStyle{
Color: color.RGBA{R: 255, A: 255},
Width: vg.Points(0.5),
}

drawBox := func(c draw.Canvas, b GlyphBox) {
x := c.X(b.X) + b.Rectangle.Min.X
y := c.Y(b.Y) + b.Rectangle.Min.Y
c.StrokeLines(sty, []vg.Point{
{X: x, Y: y},
{X: x + b.Rectangle.Size().X, Y: y},
{X: x + b.Rectangle.Size().X, Y: y + b.Rectangle.Size().Y},
{X: x, Y: y + b.Rectangle.Size().Y},
{X: x, Y: y},
})
}

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())
drawBox(dac, b)
}

p.X.sanitizeRange()
p.Y.sanitizeRange()

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

ywidth := y.size()
xheight := x.size()

cx := padX(p, draw.Crop(c, ywidth, 0, 0, 0))
for _, b := range x.GlyphBoxes(nil) {
drawBox(cx, b)
}

cy := padY(p, draw.Crop(c, 0, 0, xheight, 0))
for _, b := range y.GlyphBoxes(nil) {
drawBox(cy, b)
}
}

Expand Down
56 changes: 56 additions & 0 deletions plot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,59 @@ func TestIssue514(t *testing.T) {
})
}
}

func TestDrawGlyphBoxes(t *testing.T) {
cmpimg.CheckPlot(func() {
p := plot.New()

p.X.Min = 0
p.X.Max = 10
p.Y.Min = 0
p.Y.Max = 10

f1 := plotter.NewFunction(func(x float64) float64 { return 5 })
f1.LineStyle.Color = color.RGBA{R: 255, A: 255}

f2 := plotter.NewFunction(func(x float64) float64 { return 6 })
f2.LineStyle.Color = color.RGBA{B: 255, A: 255}

labels, err := plotter.NewLabels(plotter.XYLabels{
XYs: []plotter.XY{
{X: 2.5, Y: 2.5},
{X: 7.5, Y: 2.5},
{X: 7.5, Y: 7.5},
{X: 2.5, Y: 7.5},
},
Labels: []string{"Agg", "Bgg", "Cgg", "Dgg"},
})
if err != nil {
t.Fatalf("could not creates labels plotter: %+v", err)
}

p.Add(f1, f2, labels)
p.Add(plotter.NewGrid())

p.Legend.Add("fg1", f1)
p.Legend.Add("fg2", f2)
p.Legend.Top = true

c := vgimg.PngCanvas{
Canvas: vgimg.New(20*vg.Centimeter, 15*vg.Centimeter),
}

d := draw.New(c)
p.Draw(d)
p.DrawGlyphBoxes(d)

buf := new(bytes.Buffer)
_, err = c.WriteTo(buf)
if err != nil {
t.Fatalf("error: %+v", err)
}

err = ioutil.WriteFile("testdata/glyphbox.png", buf.Bytes(), 0644)
if err != nil {
t.Fatalf("could not save plot: %+v", err)
}
}, t, "glyphbox.png")
}
Binary file added testdata/glyphbox_golden.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit fc1c0f0

Please sign in to comment.