Skip to content

Commit b87959d

Browse files
committed
text,vg/draw: move TextHandler and TextStyle to text
1 parent 61a5e05 commit b87959d

7 files changed

Lines changed: 274 additions & 233 deletions

File tree

plot.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ var (
2222
DefaultFont = "Times-Roman"
2323

2424
// DefaultTextHandler is the default text handler used for text processing.
25-
DefaultTextHandler draw.TextHandler = text.Plain{}
25+
DefaultTextHandler text.Handler = text.Plain{}
2626
)
2727

2828
// Plot is the basic type representing a plot.

text/latex.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import (
1616
"github.com/go-latex/latex/tex"
1717

1818
"gonum.org/v1/plot/vg"
19-
"gonum.org/v1/plot/vg/draw"
2019
)
2120

2221
// Latex parses, formats and renders LaTeX.
@@ -26,7 +25,7 @@ type Latex struct {
2625
DPI float64
2726
}
2827

29-
var _ draw.TextHandler = (*Latex)(nil)
28+
var _ Handler = (*Latex)(nil)
3029

3130
// Lines splits a given block of text into separate lines.
3231
func (hdlr Latex) Lines(txt string) []string {
@@ -74,7 +73,7 @@ func (hdlr Latex) Box(txt string, fnt vg.Font) (width, height, depth vg.Length)
7473

7574
// Draw renders the given text with the provided style and position
7675
// on the canvas.
77-
func (hdlr Latex) Draw(c vg.Canvas, txt string, sty draw.TextStyle, pt vg.Point) {
76+
func (hdlr Latex) Draw(c vg.Canvas, txt string, sty TextStyle, pt vg.Point) {
7877
cnv := drawtex.New()
7978
fnts := &ttf.Fonts{
8079
Rm: sty.Font.Font(),
@@ -137,7 +136,7 @@ func (hdlr Latex) dpi() float64 {
137136

138137
type latex struct {
139138
cnv vg.Canvas
140-
sty draw.TextStyle
139+
sty TextStyle
141140
pt vg.Point
142141

143142
w vg.Length

text/plain.go

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,67 @@
44

55
package text // import "gonum.org/v1/plot/text"
66

7-
import "gonum.org/v1/plot/vg/draw"
7+
import (
8+
"math"
9+
"strings"
10+
11+
"gonum.org/v1/plot/vg"
12+
)
813

914
// Plain is a text/plain handler.
10-
type Plain = draw.PlainTextHandler
15+
type Plain struct{}
16+
17+
var _ Handler = (*Plain)(nil)
18+
19+
// Lines splits a given block of text into separate lines.
20+
func (hdlr Plain) Lines(txt string) []string {
21+
txt = strings.TrimRight(txt, "\n")
22+
return strings.Split(txt, "\n")
23+
}
24+
25+
// Box returns the bounding box of the given non-multiline text where:
26+
// - width is the horizontal space from the origin.
27+
// - height is the vertical space above the baseline.
28+
// - depth is the vertical space below the baseline, a positive number.
29+
func (hdlr Plain) Box(txt string, fnt vg.Font) (width, height, depth vg.Length) {
30+
ext := fnt.Extents()
31+
width = fnt.Width(txt)
32+
height = ext.Ascent
33+
depth = ext.Descent
34+
35+
return width, height, depth
36+
}
37+
38+
// Draw renders the given text with the provided style and position
39+
// on the canvas.
40+
func (hdlr Plain) Draw(c vg.Canvas, txt string, sty TextStyle, pt vg.Point) {
41+
txt = strings.TrimRight(txt, "\n")
42+
if len(txt) == 0 {
43+
return
44+
}
45+
46+
c.SetColor(sty.Color)
47+
48+
if sty.Rotation != 0 {
49+
c.Push()
50+
c.Rotate(sty.Rotation)
51+
}
52+
53+
sin64, cos64 := math.Sincos(sty.Rotation)
54+
cos := vg.Length(cos64)
55+
sin := vg.Length(sin64)
56+
pt.X, pt.Y = pt.Y*sin+pt.X*cos, pt.Y*cos-pt.X*sin
57+
58+
lines := hdlr.Lines(txt)
59+
ht := sty.Height(txt)
60+
pt.Y += ht*vg.Length(sty.YAlign) - sty.Font.Extents().Ascent
61+
for i, line := range lines {
62+
xoffs := vg.Length(sty.XAlign) * sty.Font.Width(line)
63+
n := vg.Length(len(lines) - i)
64+
c.FillString(sty.Font, pt.Add(vg.Point{X: xoffs, Y: n * sty.Font.Size}), line)
65+
}
1166

12-
var _ draw.TextHandler = (*Plain)(nil)
67+
if sty.Rotation != 0 {
68+
c.Pop()
69+
}
70+
}

text/text.go

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
// Copyright ©2021 The Gonum Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package text
6+
7+
import (
8+
"image/color"
9+
"math"
10+
11+
"gonum.org/v1/plot/vg"
12+
)
13+
14+
// Handler parses, formats and renders text.
15+
type Handler interface {
16+
// Lines splits a given block of text into separate lines.
17+
Lines(txt string) []string
18+
19+
// Box returns the bounding box of the given non-multiline text where:
20+
// - width is the horizontal space from the origin.
21+
// - height is the vertical space above the baseline.
22+
// - depth is the vertical space below the baseline, a positive number.
23+
Box(txt string, fnt vg.Font) (width, height, depth vg.Length)
24+
25+
// Draw renders the given text with the provided style and position
26+
// on the canvas.
27+
Draw(c vg.Canvas, txt string, sty TextStyle, pt vg.Point)
28+
}
29+
30+
// XAlignment specifies text alignment in the X direction. Three preset
31+
// options are available, but an arbitrary alignment
32+
// can also be specified using XAlignment(desired number).
33+
type XAlignment float64
34+
35+
const (
36+
// XLeft aligns the left edge of the text with the specified location.
37+
XLeft XAlignment = 0
38+
// XCenter aligns the horizontal center of the text with the specified location.
39+
XCenter XAlignment = -0.5
40+
// XRight aligns the right edge of the text with the specified location.
41+
XRight XAlignment = -1
42+
)
43+
44+
// YAlignment specifies text alignment in the Y direction. Three preset
45+
// options are available, but an arbitrary alignment
46+
// can also be specified using YAlignment(desired number).
47+
type YAlignment float64
48+
49+
const (
50+
// YTop aligns the top of of the text with the specified location.
51+
YTop YAlignment = -1
52+
// YCenter aligns the vertical center of the text with the specified location.
53+
YCenter YAlignment = -0.5
54+
// YBottom aligns the bottom of the text with the specified location.
55+
YBottom YAlignment = 0
56+
)
57+
58+
// Position specifies the text position.
59+
const (
60+
PosLeft = -1
61+
PosBottom = -1
62+
PosCenter = 0
63+
PosTop = +1
64+
PosRight = +1
65+
)
66+
67+
// TextStyle describes what text will look like.
68+
type TextStyle struct {
69+
// Color is the text color.
70+
Color color.Color
71+
72+
// Font is the font description.
73+
Font vg.Font
74+
75+
// Rotation is the text rotation in radians, performed around the axis
76+
// defined by XAlign and YAlign.
77+
Rotation float64
78+
79+
// XAlign and YAlign specify the alignment of the text.
80+
XAlign XAlignment
81+
YAlign YAlignment
82+
83+
// Handler parses and formats text according to a given
84+
// dialect (Markdown, LaTeX, plain, ...)
85+
// The default is a plain text handler.
86+
Handler Handler
87+
}
88+
89+
// Width returns the width of lines of text
90+
// when using the given font before any text rotation is applied.
91+
func (sty TextStyle) Width(txt string) (max vg.Length) {
92+
w, _ := sty.box(txt)
93+
return w
94+
}
95+
96+
// Height returns the height of the text when using
97+
// the given font before any text rotation is applied.
98+
func (sty TextStyle) Height(txt string) vg.Length {
99+
_, h := sty.box(txt)
100+
return h
101+
}
102+
103+
// box returns the bounding box of a possibly multi-line text.
104+
func (sty TextStyle) box(txt string) (w, h vg.Length) {
105+
var (
106+
lines = sty.Handler.Lines(txt)
107+
e = sty.Font.Extents()
108+
linegap = (e.Height - e.Ascent - e.Descent)
109+
)
110+
for i, line := range lines {
111+
ww, hh, dd := sty.Handler.Box(line, sty.Font)
112+
if ww > w {
113+
w = ww
114+
}
115+
h += hh + dd
116+
if i > 0 {
117+
h += linegap
118+
}
119+
}
120+
121+
return w, h
122+
}
123+
124+
// Rectangle returns a rectangle giving the bounds of
125+
// this text assuming that it is drawn at (0, 0).
126+
func (sty TextStyle) Rectangle(txt string) vg.Rectangle {
127+
e := sty.Font.Extents()
128+
w, h := sty.box(txt)
129+
desc := vg.Length(e.Height - e.Ascent) // descent + linegap
130+
xoff := vg.Length(sty.XAlign) * w
131+
yoff := vg.Length(sty.YAlign)*h - desc
132+
133+
// lower left corner
134+
p1 := rotatePoint(sty.Rotation, vg.Point{X: xoff, Y: yoff})
135+
// upper left corner
136+
p2 := rotatePoint(sty.Rotation, vg.Point{X: xoff, Y: h + yoff})
137+
// lower right corner
138+
p3 := rotatePoint(sty.Rotation, vg.Point{X: w + xoff, Y: yoff})
139+
// upper right corner
140+
p4 := rotatePoint(sty.Rotation, vg.Point{X: w + xoff, Y: h + yoff})
141+
142+
return vg.Rectangle{
143+
Max: vg.Point{
144+
X: max(p1.X, p2.X, p3.X, p4.X),
145+
Y: max(p1.Y, p2.Y, p3.Y, p4.Y),
146+
},
147+
Min: vg.Point{
148+
X: min(p1.X, p2.X, p3.X, p4.X),
149+
Y: min(p1.Y, p2.Y, p3.Y, p4.Y),
150+
},
151+
}
152+
}
153+
154+
// rotatePoint applies rotation theta (in radians) about the origin to point p.
155+
func rotatePoint(theta float64, p vg.Point) vg.Point {
156+
if theta == 0 {
157+
return p
158+
}
159+
x := float64(p.X)
160+
y := float64(p.Y)
161+
162+
sin, cos := math.Sincos(theta)
163+
164+
return vg.Point{
165+
X: vg.Length(x*cos - y*sin),
166+
Y: vg.Length(y*cos + x*sin),
167+
}
168+
}
169+
170+
func max(d ...vg.Length) vg.Length {
171+
o := vg.Length(math.Inf(-1))
172+
for _, dd := range d {
173+
if dd > o {
174+
o = dd
175+
}
176+
}
177+
return o
178+
}
179+
180+
func min(d ...vg.Length) vg.Length {
181+
o := vg.Length(math.Inf(1))
182+
for _, dd := range d {
183+
if dd < o {
184+
o = dd
185+
}
186+
}
187+
return o
188+
}

vg/draw/canvas.go

Lines changed: 15 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"sort"
1212
"sync"
1313

14+
"gonum.org/v1/plot/text"
1415
"gonum.org/v1/plot/vg"
1516
)
1617

@@ -61,38 +62,38 @@ type Canvas struct {
6162
// XAlignment specifies text alignment in the X direction. Three preset
6263
// options are available, but an arbitrary alignment
6364
// can also be specified using XAlignment(desired number).
64-
type XAlignment float64
65+
type XAlignment = text.XAlignment
6566

6667
const (
6768
// XLeft aligns the left edge of the text with the specified location.
68-
XLeft XAlignment = 0
69+
XLeft = text.XLeft
6970
// XCenter aligns the horizontal center of the text with the specified location.
70-
XCenter XAlignment = -0.5
71+
XCenter = text.XCenter
7172
// XRight aligns the right edge of the text with the specified location.
72-
XRight XAlignment = -1
73+
XRight = text.XRight
7374
)
7475

7576
// YAlignment specifies text alignment in the Y direction. Three preset
7677
// options are available, but an arbitrary alignment
7778
// can also be specified using YAlignment(desired number).
78-
type YAlignment float64
79+
type YAlignment = text.YAlignment
7980

8081
const (
8182
// YTop aligns the top of of the text with the specified location.
82-
YTop YAlignment = -1
83+
YTop = text.YTop
8384
// YCenter aligns the vertical center of the text with the specified location.
84-
YCenter YAlignment = -0.5
85+
YCenter = text.YCenter
8586
// YBottom aligns the bottom of the text with the specified location.
86-
YBottom YAlignment = 0
87+
YBottom = text.YBottom
8788
)
8889

8990
// Position specifies the text position.
9091
const (
91-
PosLeft = -1
92-
PosBottom = -1
93-
PosCenter = 0
94-
PosTop = +1
95-
PosRight = +1
92+
PosLeft = text.PosLeft
93+
PosBottom = text.PosBottom
94+
PosCenter = text.PosCenter
95+
PosTop = text.PosTop
96+
PosRight = text.PosRight
9697
)
9798

9899
// LineStyle describes what a line will look like.
@@ -628,25 +629,5 @@ func isect(p0, p1, clip, norm vg.Point) vg.Point {
628629
// FillText fills lines of text in the draw area.
629630
// pt specifies the location where the text is to be drawn.
630631
func (c *Canvas) FillText(sty TextStyle, pt vg.Point, txt string) {
631-
sty.handler().Draw(c, txt, sty, pt)
632-
}
633-
634-
func max(d ...vg.Length) vg.Length {
635-
o := vg.Length(math.Inf(-1))
636-
for _, dd := range d {
637-
if dd > o {
638-
o = dd
639-
}
640-
}
641-
return o
642-
}
643-
644-
func min(d ...vg.Length) vg.Length {
645-
o := vg.Length(math.Inf(1))
646-
for _, dd := range d {
647-
if dd < o {
648-
o = dd
649-
}
650-
}
651-
return o
632+
sty.Handler.Draw(c, txt, sty, pt)
652633
}

0 commit comments

Comments
 (0)