Skip to content

Commit 89f8c6d

Browse files
committed
plot: take axis label offset and markers descent into account in axes glyphboxes
Fixes #676. Signed-off-by: Sebastien Binet <binet@cern.ch>
1 parent 433088d commit 89f8c6d

5 files changed

Lines changed: 158 additions & 14 deletions

File tree

axis.go

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -295,15 +295,29 @@ func (a horizontalAxis) draw(c draw.Canvas) {
295295
}
296296

297297
// GlyphBoxes returns the GlyphBoxes for the tick labels.
298-
func (a horizontalAxis) GlyphBoxes(*Plot) []GlyphBox {
299-
var boxes []GlyphBox
300-
for _, t := range a.Tick.Marker.Ticks(a.Min, a.Max) {
298+
func (a horizontalAxis) GlyphBoxes(p *Plot) []GlyphBox {
299+
var (
300+
boxes []GlyphBox
301+
yoff font.Length
302+
)
303+
304+
if a.Label.Text != "" {
305+
yoff += a.Label.TextStyle.Height(a.Label.Text)
306+
yoff += a.Label.Padding
307+
}
308+
309+
var (
310+
marks = a.Tick.Marker.Ticks(a.Min, a.Max)
311+
height = tickLabelHeight(a.Tick.Label, marks)
312+
descent = a.Tick.Label.FontExtents().Descent
313+
)
314+
for _, t := range marks {
301315
if t.IsMinor() {
302316
continue
303317
}
304318
box := GlyphBox{
305319
X: a.Norm(t.Value),
306-
Rectangle: a.Tick.Label.Rectangle(t.Label),
320+
Rectangle: a.Tick.Label.Rectangle(t.Label).Add(vg.Point{Y: yoff + height + descent}),
307321
}
308322
boxes = append(boxes, box)
309323
}
@@ -396,15 +410,37 @@ func (a verticalAxis) draw(c draw.Canvas) {
396410
}
397411

398412
// GlyphBoxes returns the GlyphBoxes for the tick labels
399-
func (a verticalAxis) GlyphBoxes(*Plot) []GlyphBox {
400-
var boxes []GlyphBox
401-
for _, t := range a.Tick.Marker.Ticks(a.Min, a.Max) {
413+
func (a verticalAxis) GlyphBoxes(p *Plot) []GlyphBox {
414+
var (
415+
boxes []GlyphBox
416+
xoff font.Length
417+
)
418+
419+
if a.Label.Text != "" {
420+
sty := a.Label.TextStyle
421+
sty.Rotation += math.Pi / 2
422+
423+
xoff += a.Label.TextStyle.Height(a.Label.Text)
424+
xoff += a.Label.TextStyle.FontExtents().Descent
425+
xoff += a.Label.Padding
426+
}
427+
428+
marks := a.Tick.Marker.Ticks(a.Min, a.Max)
429+
if w := tickLabelWidth(a.Tick.Label, marks); len(marks) > 0 && w > 0 {
430+
xoff += w
431+
}
432+
433+
var (
434+
ext = a.Tick.Label.FontExtents()
435+
desc = ext.Height - ext.Ascent // descent + linegap
436+
)
437+
for _, t := range marks {
402438
if t.IsMinor() {
403439
continue
404440
}
405441
box := GlyphBox{
406442
Y: a.Norm(t.Value),
407-
Rectangle: a.Tick.Label.Rectangle(t.Label),
443+
Rectangle: a.Tick.Label.Rectangle(t.Label).Add(vg.Point{X: xoff, Y: desc}),
408444
}
409445
boxes = append(boxes, box)
410446
}

legend.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,8 +173,12 @@ func (l *Legend) Rectangle(c draw.Canvas) vg.Rectangle {
173173
// entryHeight returns the height of the tallest legend
174174
// entry text.
175175
func (l *Legend) entryHeight() (height vg.Length) {
176+
var (
177+
ext = l.TextStyle.FontExtents()
178+
desc = ext.Height - ext.Ascent // descent + linegap
179+
)
176180
for _, e := range l.entries {
177-
if h := l.TextStyle.Rectangle(e.text).Max.Y; h > height {
181+
if h := l.TextStyle.Rectangle(e.text).Max.Y + desc; h > height {
178182
height = h
179183
}
180184
}

plot.go

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -191,12 +191,60 @@ func (p *Plot) DataCanvas(da draw.Canvas) draw.Canvas {
191191

192192
// DrawGlyphBoxes draws red outlines around the plot's
193193
// GlyphBoxes. This is intended for debugging.
194-
func (p *Plot) DrawGlyphBoxes(c *draw.Canvas) {
195-
c.SetColor(color.RGBA{R: 255, A: 255})
194+
func (p *Plot) DrawGlyphBoxes(c draw.Canvas) {
195+
dac := p.DataCanvas(c)
196+
sty := draw.LineStyle{
197+
Color: color.RGBA{R: 255, A: 255},
198+
Width: vg.Points(0.5),
199+
}
200+
201+
drawBox := func(c draw.Canvas, b GlyphBox) {
202+
x := c.X(b.X) + b.Rectangle.Min.X
203+
y := c.Y(b.Y) + b.Rectangle.Min.Y
204+
c.StrokeLines(sty, []vg.Point{
205+
{X: x, Y: y},
206+
{X: x + b.Rectangle.Size().X, Y: y},
207+
{X: x + b.Rectangle.Size().X, Y: y + b.Rectangle.Size().Y},
208+
{X: x, Y: y + b.Rectangle.Size().Y},
209+
{X: x, Y: y},
210+
})
211+
}
212+
213+
var title vg.Length
214+
if p.Title.Text != "" {
215+
rect := p.Title.TextStyle.Rectangle(p.Title.Text)
216+
title += rect.Size().Y
217+
title += p.Title.Padding
218+
box := GlyphBox{
219+
Rectangle: rect.Add(vg.Point{
220+
X: c.Center().X,
221+
Y: c.Max.Y,
222+
}),
223+
}
224+
drawBox(c, box)
225+
}
226+
196227
for _, b := range p.GlyphBoxes(p) {
197-
b.Rectangle.Min.X += c.X(b.X)
198-
b.Rectangle.Min.Y += c.Y(b.Y)
199-
c.Stroke(b.Rectangle.Path())
228+
drawBox(dac, b)
229+
}
230+
231+
p.X.sanitizeRange()
232+
p.Y.sanitizeRange()
233+
234+
x := horizontalAxis{p.X}
235+
y := verticalAxis{p.Y}
236+
237+
ywidth := y.size()
238+
xheight := x.size()
239+
240+
cx := padX(p, draw.Crop(c, ywidth, 0, 0, 0))
241+
for _, b := range x.GlyphBoxes(p) {
242+
drawBox(cx, b)
243+
}
244+
245+
cy := padY(p, draw.Crop(c, 0, 0, xheight, 0))
246+
for _, b := range y.GlyphBoxes(p) {
247+
drawBox(cy, b)
200248
}
201249
}
202250

plot_test.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,3 +128,59 @@ func TestIssue514(t *testing.T) {
128128
})
129129
}
130130
}
131+
132+
func TestDrawGlyphBoxes(t *testing.T) {
133+
cmpimg.CheckPlot(func() {
134+
p := plot.New()
135+
136+
p.X.Min = 0
137+
p.X.Max = 10
138+
p.Y.Min = 0
139+
p.Y.Max = 10
140+
141+
f1 := plotter.NewFunction(func(x float64) float64 { return 5 })
142+
f1.LineStyle.Color = color.RGBA{R: 255, A: 255}
143+
144+
f2 := plotter.NewFunction(func(x float64) float64 { return 6 })
145+
f2.LineStyle.Color = color.RGBA{B: 255, A: 255}
146+
147+
labels, err := plotter.NewLabels(plotter.XYLabels{
148+
XYs: []plotter.XY{
149+
{X: 2.5, Y: 2.5},
150+
{X: 7.5, Y: 2.5},
151+
{X: 7.5, Y: 7.5},
152+
{X: 2.5, Y: 7.5},
153+
},
154+
Labels: []string{"Agg", "Bgg", "Cgg", "Dgg"},
155+
})
156+
if err != nil {
157+
t.Fatalf("could not creates labels plotter: %+v", err)
158+
}
159+
160+
p.Add(f1, f2, labels)
161+
p.Add(plotter.NewGrid())
162+
163+
p.Legend.Add("fg1", f1)
164+
p.Legend.Add("fg2", f2)
165+
p.Legend.Top = true
166+
167+
c := vgimg.PngCanvas{
168+
Canvas: vgimg.New(20*vg.Centimeter, 15*vg.Centimeter),
169+
}
170+
171+
d := draw.New(c)
172+
p.Draw(d)
173+
p.DrawGlyphBoxes(d)
174+
175+
buf := new(bytes.Buffer)
176+
_, err = c.WriteTo(buf)
177+
if err != nil {
178+
t.Fatalf("error: %+v", err)
179+
}
180+
181+
err = ioutil.WriteFile("testdata/glyphbox.png", buf.Bytes(), 0644)
182+
if err != nil {
183+
t.Fatalf("could not save plot: %+v", err)
184+
}
185+
}, t, "glyphbox.png")
186+
}

testdata/glyphbox_golden.png

9.54 KB
Loading

0 commit comments

Comments
 (0)