Skip to content

plot: multi-line title is leaking into data canvas area #680

@sbinet

Description

@sbinet

this simple plot script:

//go:build ignore
// +build ignore

package main

import (
	"flag"
	"fmt"
	"image/color"
	"log"

	"gonum.org/v1/plot"
	"gonum.org/v1/plot/plotter"
	"gonum.org/v1/plot/vg"
)

func main() {
	flag.Parse()

	sha := flag.Arg(0)

	var err error
	p, err := plot.New()
	if err != nil {
		log.Fatal(err)
	}
	p.Title.Text = "T\nmy title\nUU"
	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}

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

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

	fname := fmt.Sprintf("box-%s.png", sha)
	fmt.Printf("saving to %q...\n", fname)
	err = p.Save(20*vg.Centimeter, 15*vg.Centimeter, fname)
	if err != nil {
		log.Fatalf("error: %+v", err)
	}
}

produces (with v0.9.0):
box-111

this is coming from the change of semantics of text.Handler.Box in aa25907 that's supposed to only deal with non-multiline text.
whereas:
https://github.com/gonum/plot/blob/v0.9.0/plot.go#L150..L156

	if p.Title.Text != "" {
		descent := p.Title.TextStyle.FontExtents().Descent
		c.FillText(p.Title.TextStyle, vg.Point{X: c.Center().X, Y: c.Max.Y + descent}, p.Title.Text)
		_, h, d := p.Title.TextStyle.Handler.Box(p.Title.Text, p.Title.TextStyle.Font)
		c.Max.Y -= h + d
		c.Max.Y -= p.Title.Padding
	}

clearly doesn't handle the multi-line case.

we could introduce a Box function in text:

package text

// Box returns the bounding box of the given possibly-multiline text where:
//  - width is the horizontal space from the origin of the widest line,
//  - height is the vertical space, from the bottom of the descent up to the ascent,
//    including any needed linegaps.
func Box(hdlr Handler, txt string, fnt Font) (width, height Length) {
        var (
                lines   = hdlr.Lines(txt)
                e       = hdlr.FontExtents(fnt)
                linegap = (e.Height - e.Ascent - e.Descent)
        )
        for i, line := range lines {
                ww, hh, dd := hdlr.Box(line, fnt)
                if ww > width {
                        width = ww
                }
                height += hh + dd
                if i > 0 {
                        height += linegap
                }
        }
        return width, height
}

(which is more or less the implementation of text.Style.box, that method could then just forward to that new top-level function.)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions