Closed sbinet closed 3 years ago
I don't think offsets should be in data units. That would be a very surprising behaviour since offsetting text on a plot is a typesetting action.
It's not entirely clear to me why you need to communicate back from typesetting lengths to data units.
Also, I think that the GlyphBoxes
method should look like this,
func (l *Labels) GlyphBoxes(p *plot.Plot) []plot.GlyphBox {
bs := make([]plot.GlyphBox, len(l.Labels))
for i, label := range l.Labels {
bs[i].X = p.X.Norm(l.XYs[i].X) + float64(l.XOffset)
bs[i].Y = p.Y.Norm(l.XYs[i].Y) + float64(l.YOffset)
sty := l.TextStyle[i]
bs[i].Rectangle = sty.Rectangle(label)
}
return bs
}
with the offset added after the scaling, just as it is in the Plot
method.
I did have tried that.
with:
// +build ignore
package main
import (
"image/color"
"log"
"os"
"gonum.org/v1/plot"
"gonum.org/v1/plot/font"
"gonum.org/v1/plot/plotter"
"gonum.org/v1/plot/vg"
"gonum.org/v1/plot/vg/draw"
"gonum.org/v1/plot/vg/vgimg"
)
func main() {
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}
if true {
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", "x"},
})
if err != nil {
log.Fatalf("could not creates labels plotter: %+v", err)
}
for i := range labels.TextStyle {
sty := &labels.TextStyle[i]
sty.Font = font.From(sty.Font, 20)
}
p.Add(labels)
}
{
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{"App", "Bpp", "Cpp", "Dpp"},
})
if err != nil {
log.Fatalf("could not creates labels plotter: %+v", err)
}
for i := range labels.TextStyle {
sty := &labels.TextStyle[i]
sty.Font = font.From(sty.Font, 20)
}
labels.XOffset = 50
p.Add(labels)
}
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
c := vgimg.PngCanvas{
Canvas: vgimg.New(20*vg.Centimeter, 15*vg.Centimeter),
}
d := draw.New(c)
p.Draw(d)
p.DrawGlyphBoxes(d)
f, err := os.Create("box.png")
if err != nil {
log.Fatalf("error: %+v", err)
}
defer f.Close()
_, err = c.WriteTo(f)
if err != nil {
log.Fatalf("error: %+v", err)
}
err = f.Close()
if err != nil {
log.Fatalf("error: %+v", err)
}
}
I get:
(i.e.: the boxes around the shifted text labels are off screen and one can only see the boxes around the non-shifted labels)
That doesn't build for me using the current master.
right, I was running on top of my changes from #708.
here is the diff for running on top of HEAD
:
87c87
< p.DrawGlyphBoxes(d)
---
> p.DrawGlyphBoxes(&d)
sorry about that.
This fixes the issue, though it's ugly.
func (l *Labels) GlyphBoxes(p *plot.Plot) []plot.GlyphBox {
bs := make([]plot.GlyphBox, len(l.Labels))
offset := vg.Point{X: l.XOffset, Y: l.YOffset}
for i, label := range l.Labels {
rect := l.TextStyle[i].Rectangle(label)
rect.Min = rect.Min.Add(offset)
rect.Max = rect.Max.Add(offset)
bs[i] = plot.GlyphBox{
X: p.X.Norm(l.XYs[i].X),
Y: p.Y.Norm(l.XYs[i].Y),
Rectangle: rect,
}
}
return bs
}
Obviously, the sty.Rectangle
method needs to understand the offset, but this will do.
that worked. thanks :)
while working on the glyphboxes issue, I've noticed:
the
{X,Y}Offset
fields are invg.Length
(so, centimeters and what not). it's convenient when drawing the labels:but rather inconvenient when trying to compute the glyphbox around each label:
(the original code of
Labels.GlyphBoxes(...)
was missing the x/y-offsets)there's no way (at least, I couldn't find one) to go from
vg
units back to "canvas/user" units. (even less so when one has only aplot.Plot
in hands, w/o the correspondingdraw.Canvas
as one has inLabels.Plot(c,p)
)I see two avenues to solve this:
{X,Y}Offset
to data units (and document)GlyphBoxes
signature (and the accordingplot.GlyphBoxer
interface) to take an additionaldraw.Canvas
argument, add a way (toplot.Plot
?) to go fromvg.Length
units back to "data units".option a) is probably less wide-ranging a change (only a couple of plotters in
gonum/plot
are using these fields)(in any event, adding a method to go from
vg.Length
units back to "data units" may very well be quite convenient, though. I had the use for such a thing forgo-hep/hplot
here when trying to set labels in "normalized" coordinates.)thoughts?