1
2
3
4
5
6
7 package vgeps
8
9 import (
10 "bufio"
11 "bytes"
12 "fmt"
13 "image"
14 "image/color"
15 "io"
16 "math"
17 "time"
18
19 "gonum.org/v1/plot/font"
20 "gonum.org/v1/plot/vg"
21 "gonum.org/v1/plot/vg/draw"
22 )
23
24 func init() {
25 draw.RegisterFormat("eps", func(w, h vg.Length) vg.CanvasWriterTo {
26 return New(w, h)
27 })
28 }
29
30
31 const DPI = 72
32
33 type Canvas struct {
34 stack []context
35 w, h vg.Length
36 buf *bytes.Buffer
37 }
38
39 type context struct {
40 color color.Color
41 width vg.Length
42 dashes []vg.Length
43 offs vg.Length
44 font string
45 fsize vg.Length
46 }
47
48
49 const pr = 5
50
51
52 func New(w, h vg.Length) *Canvas {
53 return NewTitle(w, h, "")
54 }
55
56
57 func NewTitle(w, h vg.Length, title string) *Canvas {
58 c := &Canvas{
59 stack: []context{{}},
60 w: w,
61 h: h,
62 buf: new(bytes.Buffer),
63 }
64 c.buf.WriteString("%%!PS-Adobe-3.0 EPSF-3.0\n")
65 c.buf.WriteString("%%Creator gonum.org/v1/plot/vg/vgeps\n")
66 c.buf.WriteString("%%Title: " + title + "\n")
67 c.buf.WriteString(fmt.Sprintf("%%%%BoundingBox: 0 0 %.*g %.*g\n",
68 pr, w.Dots(DPI),
69 pr, h.Dots(DPI)))
70 c.buf.WriteString(fmt.Sprintf("%%%%CreationDate: %s\n", time.Now()))
71 c.buf.WriteString("%%Orientation: Portrait\n")
72 c.buf.WriteString("%%EndComments\n")
73 c.buf.WriteString("\n")
74 vg.Initialize(c)
75 return c
76 }
77
78 func (c *Canvas) Size() (w, h vg.Length) {
79 return c.w, c.h
80 }
81
82
83 func (e *Canvas) context() *context {
84 return &e.stack[len(e.stack)-1]
85 }
86
87 func (e *Canvas) SetLineWidth(w vg.Length) {
88 if e.context().width != w {
89 e.context().width = w
90 fmt.Fprintf(e.buf, "%.*g setlinewidth\n", pr, w.Dots(DPI))
91 }
92 }
93
94 func (e *Canvas) SetLineDash(dashes []vg.Length, o vg.Length) {
95 cur := e.context().dashes
96 dashEq := len(dashes) == len(cur)
97 for i := 0; dashEq && i < len(dashes); i++ {
98 if dashes[i] != cur[i] {
99 dashEq = false
100 }
101 }
102 if !dashEq || e.context().offs != o {
103 e.context().dashes = dashes
104 e.context().offs = o
105 e.buf.WriteString("[")
106 for _, d := range dashes {
107 fmt.Fprintf(e.buf, " %.*g", pr, d.Dots(DPI))
108 }
109 e.buf.WriteString(" ] ")
110 fmt.Fprintf(e.buf, "%.*g setdash\n", pr, o.Dots(DPI))
111 }
112 }
113
114 func (e *Canvas) SetColor(c color.Color) {
115 if c == nil {
116 c = color.Black
117 }
118 if e.context().color != c {
119 e.context().color = c
120 r, g, b, _ := c.RGBA()
121 mx := float64(math.MaxUint16)
122 fmt.Fprintf(e.buf, "%.*g %.*g %.*g setrgbcolor\n", pr, float64(r)/mx,
123 pr, float64(g)/mx, pr, float64(b)/mx)
124 }
125 }
126
127 func (e *Canvas) Rotate(r float64) {
128 fmt.Fprintf(e.buf, "%.*g rotate\n", pr, r*180/math.Pi)
129 }
130
131 func (e *Canvas) Translate(pt vg.Point) {
132 fmt.Fprintf(e.buf, "%.*g %.*g translate\n",
133 pr, pt.X.Dots(DPI), pr, pt.Y.Dots(DPI))
134 }
135
136 func (e *Canvas) Scale(x, y float64) {
137 fmt.Fprintf(e.buf, "%.*g %.*g scale\n", pr, x, pr, y)
138 }
139
140 func (e *Canvas) Push() {
141 e.stack = append(e.stack, *e.context())
142 e.buf.WriteString("gsave\n")
143 }
144
145 func (e *Canvas) Pop() {
146 e.stack = e.stack[:len(e.stack)-1]
147 e.buf.WriteString("grestore\n")
148 }
149
150 func (e *Canvas) Stroke(path vg.Path) {
151 if e.context().width <= 0 {
152 return
153 }
154 e.trace(path)
155 e.buf.WriteString("stroke\n")
156 }
157
158 func (e *Canvas) Fill(path vg.Path) {
159 e.trace(path)
160 e.buf.WriteString("fill\n")
161 }
162
163 func (e *Canvas) trace(path vg.Path) {
164 e.buf.WriteString("newpath\n")
165 for _, comp := range path {
166 switch comp.Type {
167 case vg.MoveComp:
168 fmt.Fprintf(e.buf, "%.*g %.*g moveto\n", pr, comp.Pos.X, pr, comp.Pos.Y)
169 case vg.LineComp:
170 fmt.Fprintf(e.buf, "%.*g %.*g lineto\n", pr, comp.Pos.X, pr, comp.Pos.Y)
171 case vg.ArcComp:
172 end := comp.Start + comp.Angle
173 arcOp := "arc"
174 if comp.Angle < 0 {
175 arcOp = "arcn"
176 }
177 fmt.Fprintf(e.buf, "%.*g %.*g %.*g %.*g %.*g %s\n", pr, comp.Pos.X, pr, comp.Pos.Y,
178 pr, comp.Radius, pr, comp.Start*180/math.Pi, pr,
179 end*180/math.Pi, arcOp)
180 case vg.CurveComp:
181 var p1, p2 vg.Point
182 switch len(comp.Control) {
183 case 1:
184 p1 = comp.Control[0]
185 p2 = p1
186 case 2:
187 p1 = comp.Control[0]
188 p2 = comp.Control[1]
189 default:
190 panic("vgeps: invalid number of control points")
191 }
192 fmt.Fprintf(e.buf, "%.*g %.*g %.*g %.*g %.*g %.*g curveto\n",
193 pr, p1.X, pr, p1.Y, pr, p2.X, pr, p2.Y, pr, comp.Pos.X, pr, comp.Pos.Y)
194 case vg.CloseComp:
195 e.buf.WriteString("closepath\n")
196 default:
197 panic(fmt.Sprintf("Unknown path component type: %d\n", comp.Type))
198 }
199 }
200 }
201
202 func (e *Canvas) FillString(fnt font.Face, pt vg.Point, str string) {
203 if e.context().font != fnt.Name() || e.context().fsize != fnt.Font.Size {
204 e.context().font = fnt.Name()
205 e.context().fsize = fnt.Font.Size
206 fmt.Fprintf(e.buf, "/%s findfont %.*g scalefont setfont\n",
207 fnt.Name(), pr, fnt.Font.Size)
208 }
209 fmt.Fprintf(e.buf, "%.*g %.*g moveto\n", pr, pt.X.Dots(DPI), pr, pt.Y.Dots(DPI))
210 fmt.Fprintf(e.buf, "(%s) show\n", str)
211 }
212
213
214 func (c *Canvas) DrawImage(rect vg.Rectangle, img image.Image) {
215
216 panic("vgeps: DrawImage not implemented")
217 }
218
219
220 func (e *Canvas) WriteTo(w io.Writer) (int64, error) {
221 b := bufio.NewWriter(w)
222 n, err := e.buf.WriteTo(b)
223 if err != nil {
224 return n, err
225 }
226 m, err := fmt.Fprintln(b, "showpage")
227 n += int64(m)
228 if err != nil {
229 return n, err
230 }
231 return n, b.Flush()
232 }
233
View as plain text