...

Source file src/gonum.org/v1/plot/vg/vgeps/vgeps.go

Documentation: gonum.org/v1/plot/vg/vgeps

     1  // Copyright ©2015 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 vgeps implements the vg.Canvas interface using
     6  // encapsulated postscript.
     7  package vgeps // import "gonum.org/v1/plot/vg/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  // DPI is the nominal resolution of drawing in EPS.
    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  // pr is the amount of precision to use when outputting float64s.
    49  const pr = 5
    50  
    51  // New returns a new Canvas.
    52  func New(w, h vg.Length) *Canvas {
    53  	return NewTitle(w, h, "")
    54  }
    55  
    56  // NewTitle returns a new Canvas with the given title string.
    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  // context returns the top context on the stack.
    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  // DrawImage implements the vg.Canvas.DrawImage method.
   214  func (c *Canvas) DrawImage(rect vg.Rectangle, img image.Image) {
   215  	// FIXME: https://github.com/gonum/plot/issues/271
   216  	panic("vgeps: DrawImage not implemented")
   217  }
   218  
   219  // WriteTo writes the canvas to an io.Writer.
   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