fixed dependencies

This commit is contained in:
nuknal
2024-10-24 15:46:01 +08:00
parent d16a5bd9c0
commit 1161e8d054
2005 changed files with 690883 additions and 0 deletions

633
vendor/gonum.org/v1/plot/vg/draw/canvas.go generated vendored Normal file
View File

@@ -0,0 +1,633 @@
// Copyright ©2015 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package draw // import "gonum.org/v1/plot/vg/draw"
import (
"fmt"
"image/color"
"math"
"sort"
"sync"
"gonum.org/v1/plot/text"
"gonum.org/v1/plot/vg"
)
// formats holds the registered canvas image formats
var formats = struct {
sync.RWMutex
m map[string]func(w, h vg.Length) vg.CanvasWriterTo
}{
m: make(map[string]func(w, h vg.Length) vg.CanvasWriterTo),
}
// Formats returns the sorted list of registered vg formats.
func Formats() []string {
formats.RLock()
defer formats.RUnlock()
list := make([]string, 0, len(formats.m))
for name := range formats.m {
list = append(list, name)
}
sort.Strings(list)
return list
}
// RegisterFormat registers an image format for use by NewFormattedCanvas.
// name is the name of the format, like "jpeg" or "png".
// fn is the construction function to call for the format.
//
// RegisterFormat panics if fn is nil.
func RegisterFormat(name string, fn func(w, h vg.Length) vg.CanvasWriterTo) {
formats.Lock()
defer formats.Unlock()
if fn == nil {
panic("draw: RegisterFormat with nil function")
}
formats.m[name] = fn
}
// A Canvas is a vector graphics canvas along with
// an associated Rectangle defining a section of the canvas
// to which drawing should take place.
type Canvas struct {
vg.Canvas
vg.Rectangle
}
// XAlignment specifies text alignment in the X direction. Three preset
// options are available, but an arbitrary alignment
// can also be specified using XAlignment(desired number).
type XAlignment = text.XAlignment
const (
// XLeft aligns the left edge of the text with the specified location.
XLeft = text.XLeft
// XCenter aligns the horizontal center of the text with the specified location.
XCenter = text.XCenter
// XRight aligns the right edge of the text with the specified location.
XRight = text.XRight
)
// YAlignment specifies text alignment in the Y direction. Three preset
// options are available, but an arbitrary alignment
// can also be specified using YAlignment(desired number).
type YAlignment = text.YAlignment
const (
// YTop aligns the top of of the text with the specified location.
YTop = text.YTop
// YCenter aligns the vertical center of the text with the specified location.
YCenter = text.YCenter
// YBottom aligns the bottom of the text with the specified location.
YBottom = text.YBottom
)
// Position specifies the text position.
const (
PosLeft = text.PosLeft
PosBottom = text.PosBottom
PosCenter = text.PosCenter
PosTop = text.PosTop
PosRight = text.PosRight
)
// LineStyle describes what a line will look like.
type LineStyle struct {
// Color is the color of the line.
Color color.Color
// Width is the width of the line.
Width vg.Length
Dashes []vg.Length
DashOffs vg.Length
}
// A GlyphStyle specifies the look of a glyph used to draw
// a point on a plot.
type GlyphStyle struct {
// Color is the color used to draw the glyph.
color.Color
// Radius specifies the size of the glyph's radius.
Radius vg.Length
// Shape draws the shape of the glyph.
Shape GlyphDrawer
}
// A GlyphDrawer wraps the DrawGlyph function.
type GlyphDrawer interface {
// DrawGlyph draws the glyph at the given
// point, with the given color and radius.
DrawGlyph(*Canvas, GlyphStyle, vg.Point)
}
// DrawGlyph draws the given glyph to the draw
// area. If the point is not within the Canvas
// or the sty.Shape is nil then nothing is drawn.
func (c *Canvas) DrawGlyph(sty GlyphStyle, pt vg.Point) {
if sty.Shape == nil || !c.Contains(pt) {
return
}
c.SetColor(sty.Color)
sty.Shape.DrawGlyph(c, sty, pt)
}
// DrawGlyphNoClip draws the given glyph to the draw
// area. If the sty.Shape is nil then nothing is drawn.
func (c *Canvas) DrawGlyphNoClip(sty GlyphStyle, pt vg.Point) {
if sty.Shape == nil {
return
}
c.SetColor(sty.Color)
sty.Shape.DrawGlyph(c, sty, pt)
}
// Rectangle returns the rectangle surrounding this glyph,
// assuming that it is drawn centered at 0,0
func (g GlyphStyle) Rectangle() vg.Rectangle {
return vg.Rectangle{
Min: vg.Point{X: -g.Radius, Y: -g.Radius},
Max: vg.Point{X: +g.Radius, Y: +g.Radius},
}
}
// CircleGlyph is a glyph that draws a solid circle.
type CircleGlyph struct{}
// DrawGlyph implements the GlyphDrawer interface.
func (CircleGlyph) DrawGlyph(c *Canvas, sty GlyphStyle, pt vg.Point) {
p := make(vg.Path, 0, 3)
p.Move(vg.Point{X: pt.X + sty.Radius, Y: pt.Y})
p.Arc(pt, sty.Radius, 0, 2*math.Pi)
p.Close()
c.Fill(p)
}
// RingGlyph is a glyph that draws the outline of a circle.
type RingGlyph struct{}
// DrawGlyph implements the Glyph interface.
func (RingGlyph) DrawGlyph(c *Canvas, sty GlyphStyle, pt vg.Point) {
c.SetLineStyle(LineStyle{Color: sty.Color, Width: vg.Points(0.5)})
p := make(vg.Path, 0, 3)
p.Move(vg.Point{X: pt.X + sty.Radius, Y: pt.Y})
p.Arc(pt, sty.Radius, 0, 2*math.Pi)
p.Close()
c.Stroke(p)
}
const (
cosπover4 = vg.Length(.707106781202420)
sinπover6 = vg.Length(.500000000025921)
cosπover6 = vg.Length(.866025403769473)
)
// SquareGlyph is a glyph that draws the outline of a square.
type SquareGlyph struct{}
// DrawGlyph implements the Glyph interface.
func (SquareGlyph) DrawGlyph(c *Canvas, sty GlyphStyle, pt vg.Point) {
c.SetLineStyle(LineStyle{Color: sty.Color, Width: vg.Points(0.5)})
x := (sty.Radius-sty.Radius*cosπover4)/2 + sty.Radius*cosπover4
p := make(vg.Path, 0, 5)
p.Move(vg.Point{X: pt.X - x, Y: pt.Y - x})
p.Line(vg.Point{X: pt.X + x, Y: pt.Y - x})
p.Line(vg.Point{X: pt.X + x, Y: pt.Y + x})
p.Line(vg.Point{X: pt.X - x, Y: pt.Y + x})
p.Close()
c.Stroke(p)
}
// BoxGlyph is a glyph that draws a filled square.
type BoxGlyph struct{}
// DrawGlyph implements the Glyph interface.
func (BoxGlyph) DrawGlyph(c *Canvas, sty GlyphStyle, pt vg.Point) {
x := (sty.Radius-sty.Radius*cosπover4)/2 + sty.Radius*cosπover4
p := make(vg.Path, 0, 5)
p.Move(vg.Point{X: pt.X - x, Y: pt.Y - x})
p.Line(vg.Point{X: pt.X + x, Y: pt.Y - x})
p.Line(vg.Point{X: pt.X + x, Y: pt.Y + x})
p.Line(vg.Point{X: pt.X - x, Y: pt.Y + x})
p.Close()
c.Fill(p)
}
// TriangleGlyph is a glyph that draws the outline of a triangle.
type TriangleGlyph struct{}
// DrawGlyph implements the Glyph interface.
func (TriangleGlyph) DrawGlyph(c *Canvas, sty GlyphStyle, pt vg.Point) {
c.SetLineStyle(LineStyle{Color: sty.Color, Width: vg.Points(0.5)})
r := sty.Radius + (sty.Radius-sty.Radius*sinπover6)/2
p := make(vg.Path, 0, 4)
p.Move(vg.Point{X: pt.X, Y: pt.Y + r})
p.Line(vg.Point{X: pt.X - r*cosπover6, Y: pt.Y - r*sinπover6})
p.Line(vg.Point{X: pt.X + r*cosπover6, Y: pt.Y - r*sinπover6})
p.Close()
c.Stroke(p)
}
// PyramidGlyph is a glyph that draws a filled triangle.
type PyramidGlyph struct{}
// DrawGlyph implements the Glyph interface.
func (PyramidGlyph) DrawGlyph(c *Canvas, sty GlyphStyle, pt vg.Point) {
r := sty.Radius + (sty.Radius-sty.Radius*sinπover6)/2
p := make(vg.Path, 0, 4)
p.Move(vg.Point{X: pt.X, Y: pt.Y + r})
p.Line(vg.Point{X: pt.X - r*cosπover6, Y: pt.Y - r*sinπover6})
p.Line(vg.Point{X: pt.X + r*cosπover6, Y: pt.Y - r*sinπover6})
p.Close()
c.Fill(p)
}
// PlusGlyph is a glyph that draws a plus sign
type PlusGlyph struct{}
// DrawGlyph implements the Glyph interface.
func (PlusGlyph) DrawGlyph(c *Canvas, sty GlyphStyle, pt vg.Point) {
c.SetLineStyle(LineStyle{Color: sty.Color, Width: vg.Points(0.5)})
r := sty.Radius
p := make(vg.Path, 0, 2)
p.Move(vg.Point{X: pt.X, Y: pt.Y + r})
p.Line(vg.Point{X: pt.X, Y: pt.Y - r})
c.Stroke(p)
p = p[:0]
p.Move(vg.Point{X: pt.X - r, Y: pt.Y})
p.Line(vg.Point{X: pt.X + r, Y: pt.Y})
c.Stroke(p)
}
// CrossGlyph is a glyph that draws a big X.
type CrossGlyph struct{}
// DrawGlyph implements the Glyph interface.
func (CrossGlyph) DrawGlyph(c *Canvas, sty GlyphStyle, pt vg.Point) {
c.SetLineStyle(LineStyle{Color: sty.Color, Width: vg.Points(0.5)})
r := sty.Radius * cosπover4
p := make(vg.Path, 0, 2)
p.Move(vg.Point{X: pt.X - r, Y: pt.Y - r})
p.Line(vg.Point{X: pt.X + r, Y: pt.Y + r})
c.Stroke(p)
p = p[:0]
p.Move(vg.Point{X: pt.X - r, Y: pt.Y + r})
p.Line(vg.Point{X: pt.X + r, Y: pt.Y - r})
c.Stroke(p)
}
// New returns a new (bounded) draw.Canvas.
func New(c vg.CanvasSizer) Canvas {
w, h := c.Size()
return NewCanvas(c, w, h)
}
// NewFormattedCanvas creates a new vg.CanvasWriterTo with the specified
// image format. Supported formats need to be registered by importing one or
// more of the following packages:
//
// - gonum.org/v1/plot/vg/vgeps: provides eps
// - gonum.org/v1/plot/vg/vgimg: provides png, jpg|jpeg, tif|tiff
// - gonum.org/v1/plot/vg/vgpdf: provides pdf
// - gonum.org/v1/plot/vg/vgsvg: provides svg
// - gonum.org/v1/plot/vg/vgtex: provides tex
func NewFormattedCanvas(w, h vg.Length, format string) (vg.CanvasWriterTo, error) {
formats.RLock()
defer formats.RUnlock()
for name, fn := range formats.m {
if format != name {
continue
}
return fn(w, h), nil
}
return nil, fmt.Errorf("unsupported format: %q", format)
}
// NewCanvas returns a new (bounded) draw.Canvas of the given size.
func NewCanvas(c vg.Canvas, w, h vg.Length) Canvas {
return Canvas{
Canvas: c,
Rectangle: vg.Rectangle{
Min: vg.Point{X: 0, Y: 0},
Max: vg.Point{X: w, Y: h},
},
}
}
// Center returns the center point of the area
func (c *Canvas) Center() vg.Point {
return vg.Point{
X: (c.Max.X-c.Min.X)/2 + c.Min.X,
Y: (c.Max.Y-c.Min.Y)/2 + c.Min.Y,
}
}
// Contains returns true if the Canvas contains the point.
func (c *Canvas) Contains(p vg.Point) bool {
return c.ContainsX(p.X) && c.ContainsY(p.Y)
}
// ContainsX returns true if the Canvas contains the
// x coordinate.
func (c *Canvas) ContainsX(x vg.Length) bool {
return x <= c.Max.X+slop && x >= c.Min.X-slop
}
// ContainsY returns true if the Canvas contains the
// y coordinate.
func (c *Canvas) ContainsY(y vg.Length) bool {
return y <= c.Max.Y+slop && y >= c.Min.Y-slop
}
// X returns the value of x, given in the unit range,
// in the drawing coordinates of this draw area.
// A value of 0, for example, will return the minimum
// x value of the draw area and a value of 1 will
// return the maximum.
func (c *Canvas) X(x float64) vg.Length {
return vg.Length(x)*(c.Max.X-c.Min.X) + c.Min.X
}
// Y returns the value of x, given in the unit range,
// in the drawing coordinates of this draw area.
// A value of 0, for example, will return the minimum
// y value of the draw area and a value of 1 will
// return the maximum.
func (c *Canvas) Y(y float64) vg.Length {
return vg.Length(y)*(c.Max.Y-c.Min.Y) + c.Min.Y
}
// Crop returns a new Canvas corresponding to the Canvas
// c with the given lengths added to the minimum
// and maximum x and y values of the Canvas's Rectangle.
// Note that cropping the right and top sides of the canvas
// requires specifying negative values of right and top.
func Crop(c Canvas, left, right, bottom, top vg.Length) Canvas {
minpt := vg.Point{
X: c.Min.X + left,
Y: c.Min.Y + bottom,
}
maxpt := vg.Point{
X: c.Max.X + right,
Y: c.Max.Y + top,
}
return Canvas{
Canvas: c,
Rectangle: vg.Rectangle{Min: minpt, Max: maxpt},
}
}
// Tiles creates regular subcanvases from a Canvas.
type Tiles struct {
// Cols and Rows specify the number of rows and columns of tiles.
Cols, Rows int
// PadTop, PadBottom, PadRight, and PadLeft specify the padding
// on the corresponding side of each tile.
PadTop, PadBottom, PadRight, PadLeft vg.Length
// PadX and PadY specify the padding between columns and rows
// of tiles respectively..
PadX, PadY vg.Length
}
// At returns the subcanvas within c that corresponds to the
// tile at column x, row y.
func (ts Tiles) At(c Canvas, x, y int) Canvas {
tileH := (c.Max.Y - c.Min.Y - ts.PadTop - ts.PadBottom -
vg.Length(ts.Rows-1)*ts.PadY) / vg.Length(ts.Rows)
tileW := (c.Max.X - c.Min.X - ts.PadLeft - ts.PadRight -
vg.Length(ts.Cols-1)*ts.PadX) / vg.Length(ts.Cols)
ymax := c.Max.Y - ts.PadTop - vg.Length(y)*(ts.PadY+tileH)
ymin := ymax - tileH
xmin := c.Min.X + ts.PadLeft + vg.Length(x)*(ts.PadX+tileW)
xmax := xmin + tileW
return Canvas{
Canvas: vg.Canvas(c),
Rectangle: vg.Rectangle{
Min: vg.Point{X: xmin, Y: ymin},
Max: vg.Point{X: xmax, Y: ymax},
},
}
}
// SetLineStyle sets the current line style
func (c *Canvas) SetLineStyle(sty LineStyle) {
c.SetColor(sty.Color)
c.SetLineWidth(sty.Width)
c.SetLineDash(sty.Dashes, sty.DashOffs)
}
// StrokeLines draws a line connecting a set of points
// in the given Canvas.
func (c *Canvas) StrokeLines(sty LineStyle, lines ...[]vg.Point) {
if len(lines) == 0 {
return
}
c.SetLineStyle(sty)
for _, l := range lines {
if len(l) == 0 {
continue
}
p := make(vg.Path, 0, len(l))
p.Move(l[0])
for _, pt := range l[1:] {
p.Line(pt)
}
c.Stroke(p)
}
}
// StrokeLine2 draws a line between two points in the given
// Canvas.
func (c *Canvas) StrokeLine2(sty LineStyle, x0, y0, x1, y1 vg.Length) {
c.StrokeLines(sty, []vg.Point{{X: x0, Y: y0}, {X: x1, Y: y1}})
}
// ClipLinesXY returns a slice of lines that
// represent the given line clipped in both
// X and Y directions.
func (c *Canvas) ClipLinesXY(lines ...[]vg.Point) [][]vg.Point {
return c.ClipLinesY(c.ClipLinesX(lines...)...)
}
// ClipLinesX returns a slice of lines that
// represent the given line clipped in the
// X direction.
func (c *Canvas) ClipLinesX(lines ...[]vg.Point) (clipped [][]vg.Point) {
lines1 := make([][]vg.Point, 0, len(lines))
for _, line := range lines {
ls := clipLine(isLeft, vg.Point{X: c.Max.X, Y: c.Min.Y}, vg.Point{X: -1, Y: 0}, line)
lines1 = append(lines1, ls...)
}
clipped = make([][]vg.Point, 0, len(lines1))
for _, line := range lines1 {
ls := clipLine(isRight, vg.Point{X: c.Min.X, Y: c.Min.Y}, vg.Point{X: 1, Y: 0}, line)
clipped = append(clipped, ls...)
}
return
}
// ClipLinesY returns a slice of lines that
// represent the given line clipped in the
// Y direction.
func (c *Canvas) ClipLinesY(lines ...[]vg.Point) (clipped [][]vg.Point) {
lines1 := make([][]vg.Point, 0, len(lines))
for _, line := range lines {
ls := clipLine(isAbove, vg.Point{X: c.Min.X, Y: c.Min.Y}, vg.Point{X: 0, Y: -1}, line)
lines1 = append(lines1, ls...)
}
clipped = make([][]vg.Point, 0, len(lines1))
for _, line := range lines1 {
ls := clipLine(isBelow, vg.Point{X: c.Min.X, Y: c.Max.Y}, vg.Point{X: 0, Y: 1}, line)
clipped = append(clipped, ls...)
}
return
}
// clipLine performs clipping of a line by a single
// clipping line specified by the norm, clip point,
// and in function.
func clipLine(in func(vg.Point, vg.Point) bool, clip, norm vg.Point, pts []vg.Point) (lines [][]vg.Point) {
l := make([]vg.Point, 0, len(pts))
for i := 1; i < len(pts); i++ {
cur, next := pts[i-1], pts[i]
curIn, nextIn := in(cur, clip), in(next, clip)
switch {
case curIn && nextIn:
l = append(l, cur)
case curIn && !nextIn:
l = append(l, cur, isect(cur, next, clip, norm))
lines = append(lines, l)
l = []vg.Point{}
case !curIn && !nextIn:
// do nothing
default: // !curIn && nextIn
l = append(l, isect(cur, next, clip, norm))
}
if nextIn && i == len(pts)-1 {
l = append(l, next)
}
}
if len(l) > 1 {
lines = append(lines, l)
}
return
}
// FillPolygon fills a polygon with the given color.
func (c *Canvas) FillPolygon(clr color.Color, pts []vg.Point) {
if len(pts) == 0 {
return
}
c.SetColor(clr)
p := make(vg.Path, 0, len(pts)+1)
p.Move(pts[0])
for _, pt := range pts[1:] {
p.Line(pt)
}
p.Close()
c.Fill(p)
}
// ClipPolygonXY returns a slice of lines that
// represent the given polygon clipped in both
// X and Y directions.
func (c *Canvas) ClipPolygonXY(pts []vg.Point) []vg.Point {
return c.ClipPolygonY(c.ClipPolygonX(pts))
}
// ClipPolygonX returns a slice of lines that
// represent the given polygon clipped in the
// X direction.
func (c *Canvas) ClipPolygonX(pts []vg.Point) []vg.Point {
return clipPoly(isLeft, vg.Point{X: c.Max.X, Y: c.Min.Y}, vg.Point{X: -1, Y: 0},
clipPoly(isRight, vg.Point{X: c.Min.X, Y: c.Min.Y}, vg.Point{X: 1, Y: 0}, pts))
}
// ClipPolygonY returns a slice of lines that
// represent the given polygon clipped in the
// Y direction.
func (c *Canvas) ClipPolygonY(pts []vg.Point) []vg.Point {
return clipPoly(isBelow, vg.Point{X: c.Min.X, Y: c.Max.Y}, vg.Point{X: 0, Y: 1},
clipPoly(isAbove, vg.Point{X: c.Min.X, Y: c.Min.Y}, vg.Point{X: 0, Y: -1}, pts))
}
// clipPoly performs clipping of a polygon by a single
// clipping line specified by the norm, clip point,
// and in function.
func clipPoly(in func(vg.Point, vg.Point) bool, clip, norm vg.Point, pts []vg.Point) (clipped []vg.Point) {
clipped = make([]vg.Point, 0, len(pts))
for i := 0; i < len(pts); i++ {
j := i + 1
if i == len(pts)-1 {
j = 0
}
cur, next := pts[i], pts[j]
curIn, nextIn := in(cur, clip), in(next, clip)
switch {
case curIn && nextIn:
clipped = append(clipped, cur)
case curIn && !nextIn:
clipped = append(clipped, cur, isect(cur, next, clip, norm))
case !curIn && !nextIn:
// do nothing
default: // !curIn && nextIn
clipped = append(clipped, isect(cur, next, clip, norm))
}
}
n := len(clipped)
return clipped[:n:n]
}
// slop is some slop for floating point equality
const slop = 3e-8 // ≈ √1⁻¹⁵
func isLeft(p, clip vg.Point) bool {
return p.X <= clip.X+slop
}
func isRight(p, clip vg.Point) bool {
return p.X >= clip.X-slop
}
func isBelow(p, clip vg.Point) bool {
return p.Y <= clip.Y+slop
}
func isAbove(p, clip vg.Point) bool {
return p.Y >= clip.Y-slop
}
// isect returns the intersection of a line p0→p1 with the
// clipping line specified by the clip point and normal.
func isect(p0, p1, clip, norm vg.Point) vg.Point {
// t = (norm · (p0 - clip)) / (norm · (p0 - p1))
t := p0.Sub(clip).Dot(norm) / p0.Sub(p1).Dot(norm)
// p = p0 + t*(p1 - p0)
return p1.Sub(p0).Scale(t).Add(p0)
}
// FillText fills lines of text in the draw area.
// pt specifies the location where the text is to be drawn.
func (c *Canvas) FillText(sty TextStyle, pt vg.Point, txt string) {
sty.Handler.Draw(c, txt, sty, pt)
}

6
vendor/gonum.org/v1/plot/vg/draw/doc.go generated vendored Normal file
View File

@@ -0,0 +1,6 @@
// Copyright ©2021 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package draw provides types and functions to draw shapes on a vg.Canvas.
package draw

12
vendor/gonum.org/v1/plot/vg/draw/text.go generated vendored Normal file
View File

@@ -0,0 +1,12 @@
// Copyright ©2020 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package draw // import "gonum.org/v1/plot/vg/draw"
import (
"gonum.org/v1/plot/text"
)
type TextHandler = text.Handler
type TextStyle = text.Style

12
vendor/gonum.org/v1/plot/vg/draw/text_plain.go generated vendored Normal file
View File

@@ -0,0 +1,12 @@
// Copyright ©2020 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package draw // import "gonum.org/v1/plot/vg/draw"
import "gonum.org/v1/plot/text"
// PlainTextHandler is a text/plain handler.
type PlainTextHandler = text.Plain
var _ text.Handler = (*PlainTextHandler)(nil)

66
vendor/gonum.org/v1/plot/vg/geom.go generated vendored Normal file
View File

@@ -0,0 +1,66 @@
// Copyright ©2016 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package vg
// A Point is a location in 2d space.
//
// Points are used for drawing, not for data. For
// data, see the XYer interface.
type Point struct {
X, Y Length
}
// Dot returns the dot product of two points.
func (p Point) Dot(q Point) Length {
return p.X*q.X + p.Y*q.Y
}
// Add returns the component-wise sum of two points.
func (p Point) Add(q Point) Point {
return Point{p.X + q.X, p.Y + q.Y}
}
// Sub returns the component-wise difference of two points.
func (p Point) Sub(q Point) Point {
return Point{p.X - q.X, p.Y - q.Y}
}
// Scale returns the component-wise product of a point and a scalar.
func (p Point) Scale(s Length) Point {
return Point{p.X * s, p.Y * s}
}
// A Rectangle represents a rectangular region of 2d space.
type Rectangle struct {
Min Point
Max Point
}
// Size returns the width and height of a Rectangle.
func (r Rectangle) Size() Point {
return Point{
X: r.Max.X - r.Min.X,
Y: r.Max.Y - r.Min.Y,
}
}
// Add returns the rectangle r translated by p.
func (r Rectangle) Add(p Point) Rectangle {
return Rectangle{
Min: r.Min.Add(p),
Max: r.Max.Add(p),
}
}
// Path returns the path of a Rect specified by its
// upper left corner, width and height.
func (r Rectangle) Path() (p Path) {
p.Move(r.Min)
p.Line(Point{X: r.Max.X, Y: r.Min.Y})
p.Line(r.Max)
p.Line(Point{X: r.Min.X, Y: r.Max.Y})
p.Close()
return
}

37
vendor/gonum.org/v1/plot/vg/len.go generated vendored Normal file
View File

@@ -0,0 +1,37 @@
// Copyright ©2015 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package vg
import "gonum.org/v1/plot/font"
// A Length is a unit-independent representation of length.
// Internally, the length is stored in postscript points.
type Length = font.Length
// Points returns a length for the given number of points.
func Points(pt float64) Length {
return font.Points(pt)
}
// Common lengths.
const (
Inch = font.Inch
Centimeter = font.Centimeter
Millimeter = font.Millimeter
)
// ParseLength parses a Length string.
// A Length string is a possible signed floating number with a unit.
// e.g. "42cm" "2.4in" "66pt"
// If no unit was given, ParseLength assumes it was (postscript) points.
// Currently valid units are:
//
// - mm (millimeter)
// - cm (centimeter)
// - in (inch)
// - pt (point)
func ParseLength(value string) (Length, error) {
return font.ParseLength(value)
}

129
vendor/gonum.org/v1/plot/vg/tee.go generated vendored Normal file
View File

@@ -0,0 +1,129 @@
// Copyright ©2020 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package vg
import (
"image"
"image/color"
"gonum.org/v1/plot/font"
)
// MultiCanvas creates a canvas that duplicates its drawing operations to all
// the provided canvases, similar to the Unix tee(1) command.
//
// Each drawing operation is sent to each listed canvas, one at a time.
func MultiCanvas(cs ...Canvas) Canvas {
return teeCanvas{cs}
}
type teeCanvas struct {
cs []Canvas
}
// SetLineWidth sets the width of stroked paths.
// If the width is not positive then stroked lines
// are not drawn.
func (tee teeCanvas) SetLineWidth(w Length) {
for _, c := range tee.cs {
c.SetLineWidth(w)
}
}
// SetLineDash sets the dash pattern for lines.
// The pattern slice specifies the lengths of
// alternating dashes and gaps, and the offset
// specifies the distance into the dash pattern
// to start the dash.
func (tee teeCanvas) SetLineDash(pattern []Length, offset Length) {
for _, c := range tee.cs {
c.SetLineDash(pattern, offset)
}
}
// SetColor sets the current drawing color.
// Note that fill color and stroke color are
// the same, so if you want different fill
// and stroke colors then you must set a color,
// draw fills, set a new color and then draw lines.
func (tee teeCanvas) SetColor(c color.Color) {
for _, canvas := range tee.cs {
canvas.SetColor(c)
}
}
// Rotate applies a rotation transform to the context.
// The parameter is specified in radians.
func (tee teeCanvas) Rotate(rad float64) {
for _, c := range tee.cs {
c.Rotate(rad)
}
}
// Translate applies a translational transform to the context.
func (tee teeCanvas) Translate(pt Point) {
for _, c := range tee.cs {
c.Translate(pt)
}
}
// Scale applies a scaling transform to the context.
func (tee teeCanvas) Scale(x, y float64) {
for _, c := range tee.cs {
c.Scale(x, y)
}
}
// Push saves the current line width, the
// current dash pattern, the current
// transforms, and the current color
// onto a stack so that the state can later
// be restored by calling Pop().
func (tee teeCanvas) Push() {
for _, c := range tee.cs {
c.Push()
}
}
// Pop restores the context saved by the
// corresponding call to Push().
func (tee teeCanvas) Pop() {
for _, c := range tee.cs {
c.Pop()
}
}
// Stroke strokes the given path.
func (tee teeCanvas) Stroke(p Path) {
for _, c := range tee.cs {
c.Stroke(p)
}
}
// Fill fills the given path.
func (tee teeCanvas) Fill(p Path) {
for _, c := range tee.cs {
c.Fill(p)
}
}
// FillString fills in text at the specified
// location using the given font.
// If the font size is zero, the text is not drawn.
func (tee teeCanvas) FillString(f font.Face, pt Point, text string) {
for _, c := range tee.cs {
c.FillString(f, pt, text)
}
}
// DrawImage draws the image, scaled to fit
// the destination rectangle.
func (tee teeCanvas) DrawImage(rect Rectangle, img image.Image) {
for _, c := range tee.cs {
c.DrawImage(rect, img)
}
}
var _ Canvas = (*teeCanvas)(nil)

189
vendor/gonum.org/v1/plot/vg/vg.go generated vendored Normal file
View File

@@ -0,0 +1,189 @@
// Copyright ©2015 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package vg defines an interface for drawing 2D vector graphics.
// This package is designed with the hope that many different
// vector graphics back-ends can conform to the interface.
package vg // import "gonum.org/v1/plot/vg"
import (
"image"
"image/color"
"io"
"gonum.org/v1/plot/font"
)
// A Canvas is the main drawing interface for 2D vector
// graphics.
// The origin is in the bottom left corner.
type Canvas interface {
// SetLineWidth sets the width of stroked paths.
// If the width is not positive then stroked lines
// are not drawn.
//
// The initial line width is 1 point.
SetLineWidth(Length)
// SetLineDash sets the dash pattern for lines.
// The pattern slice specifies the lengths of
// alternating dashes and gaps, and the offset
// specifies the distance into the dash pattern
// to start the dash.
//
// The initial dash pattern is a solid line.
SetLineDash(pattern []Length, offset Length)
// SetColor sets the current drawing color.
// Note that fill color and stroke color are
// the same, so if you want different fill
// and stroke colors then you must set a color,
// draw fills, set a new color and then draw lines.
//
// The initial color is black.
// If SetColor is called with a nil color then black is used.
SetColor(color.Color)
// Rotate applies a rotation transform to the context.
// The parameter is specified in radians.
Rotate(rad float64)
// Translate applies a translational transform
// to the context.
Translate(pt Point)
// Scale applies a scaling transform to the
// context.
Scale(x, y float64)
// Push saves the current line width, the
// current dash pattern, the current
// transforms, and the current color
// onto a stack so that the state can later
// be restored by calling Pop().
Push()
// Pop restores the context saved by the
// corresponding call to Push().
Pop()
// Stroke strokes the given path.
Stroke(Path)
// Fill fills the given path.
Fill(Path)
// FillString fills in text at the specified
// location using the given font.
// If the font size is zero, the text is not drawn.
FillString(f font.Face, pt Point, text string)
// DrawImage draws the image, scaled to fit
// the destination rectangle.
DrawImage(rect Rectangle, img image.Image)
}
// CanvasSizer is a Canvas with a defined size.
type CanvasSizer interface {
Canvas
Size() (x, y Length)
}
// CanvasWriterTo is a CanvasSizer with a WriteTo method.
type CanvasWriterTo interface {
CanvasSizer
io.WriterTo
}
// Initialize sets all of the canvas's values to their
// initial values.
func Initialize(c Canvas) {
c.SetLineWidth(Points(1))
c.SetLineDash([]Length{}, 0)
c.SetColor(color.Black)
}
type Path []PathComp
// Move moves the current location of the path to
// the given point.
func (p *Path) Move(pt Point) {
*p = append(*p, PathComp{Type: MoveComp, Pos: pt})
}
// Line draws a line from the current point to the
// given point.
func (p *Path) Line(pt Point) {
*p = append(*p, PathComp{Type: LineComp, Pos: pt})
}
// Arc adds an arc to the path defined by the center
// point of the arc's circle, the radius of the circle
// and the start and sweep angles.
func (p *Path) Arc(pt Point, rad Length, s, a float64) {
*p = append(*p, PathComp{
Type: ArcComp,
Pos: pt,
Radius: rad,
Start: s,
Angle: a,
})
}
// QuadTo adds a quadratic curve element to the path,
// given by the control point p1 and end point pt.
func (p *Path) QuadTo(p1, pt Point) {
*p = append(*p, PathComp{Type: CurveComp, Pos: pt, Control: []Point{p1}})
}
// CubeTo adds a cubic curve element to the path,
// given by the control points p1 and p2 and the end point pt.
func (p *Path) CubeTo(p1, p2, pt Point) {
*p = append(*p, PathComp{Type: CurveComp, Pos: pt, Control: []Point{p1, p2}})
}
// Close closes the path by connecting the current
// location to the start location with a line.
func (p *Path) Close() {
*p = append(*p, PathComp{Type: CloseComp})
}
// Constants that tag the type of each path
// component.
const (
MoveComp = iota
LineComp
ArcComp
CurveComp
CloseComp
)
// A PathComp is a component of a path structure.
type PathComp struct {
// Type is the type of a particluar component.
// Based on the type, each of the following
// fields may have a different meaning or may
// be meaningless.
Type int
// The Pos field is used as the destination
// of a MoveComp or LineComp and is the center
// point of an ArcComp. It is not used in
// the CloseComp.
Pos Point
// Control is one or two intermediate points
// for a CurveComp used by QuadTo and CubeTo.
Control []Point
// Radius is only used for ArcComps, it is
// the radius of the circle defining the arc.
Radius Length
// Start and Angle are only used for ArcComps.
// They define the start angle and sweep angle of
// the arc around the circle. The units of the
// angle are radians.
Start, Angle float64
}

232
vendor/gonum.org/v1/plot/vg/vgeps/vgeps.go generated vendored Normal file
View File

@@ -0,0 +1,232 @@
// Copyright ©2015 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package vgeps implements the vg.Canvas interface using
// encapsulated postscript.
package vgeps // import "gonum.org/v1/plot/vg/vgeps"
import (
"bufio"
"bytes"
"fmt"
"image"
"image/color"
"io"
"math"
"time"
"gonum.org/v1/plot/font"
"gonum.org/v1/plot/vg"
"gonum.org/v1/plot/vg/draw"
)
func init() {
draw.RegisterFormat("eps", func(w, h vg.Length) vg.CanvasWriterTo {
return New(w, h)
})
}
// DPI is the nominal resolution of drawing in EPS.
const DPI = 72
type Canvas struct {
stack []context
w, h vg.Length
buf *bytes.Buffer
}
type context struct {
color color.Color
width vg.Length
dashes []vg.Length
offs vg.Length
font string
fsize vg.Length
}
// pr is the amount of precision to use when outputting float64s.
const pr = 5
// New returns a new Canvas.
func New(w, h vg.Length) *Canvas {
return NewTitle(w, h, "")
}
// NewTitle returns a new Canvas with the given title string.
func NewTitle(w, h vg.Length, title string) *Canvas {
c := &Canvas{
stack: []context{{}},
w: w,
h: h,
buf: new(bytes.Buffer),
}
c.buf.WriteString("%%!PS-Adobe-3.0 EPSF-3.0\n")
c.buf.WriteString("%%Creator gonum.org/v1/plot/vg/vgeps\n")
c.buf.WriteString("%%Title: " + title + "\n")
c.buf.WriteString(fmt.Sprintf("%%%%BoundingBox: 0 0 %.*g %.*g\n",
pr, w.Dots(DPI),
pr, h.Dots(DPI)))
c.buf.WriteString(fmt.Sprintf("%%%%CreationDate: %s\n", time.Now()))
c.buf.WriteString("%%Orientation: Portrait\n")
c.buf.WriteString("%%EndComments\n")
c.buf.WriteString("\n")
vg.Initialize(c)
return c
}
func (c *Canvas) Size() (w, h vg.Length) {
return c.w, c.h
}
// context returns the top context on the stack.
func (e *Canvas) context() *context {
return &e.stack[len(e.stack)-1]
}
func (e *Canvas) SetLineWidth(w vg.Length) {
if e.context().width != w {
e.context().width = w
fmt.Fprintf(e.buf, "%.*g setlinewidth\n", pr, w.Dots(DPI))
}
}
func (e *Canvas) SetLineDash(dashes []vg.Length, o vg.Length) {
cur := e.context().dashes
dashEq := len(dashes) == len(cur)
for i := 0; dashEq && i < len(dashes); i++ {
if dashes[i] != cur[i] {
dashEq = false
}
}
if !dashEq || e.context().offs != o {
e.context().dashes = dashes
e.context().offs = o
e.buf.WriteString("[")
for _, d := range dashes {
fmt.Fprintf(e.buf, " %.*g", pr, d.Dots(DPI))
}
e.buf.WriteString(" ] ")
fmt.Fprintf(e.buf, "%.*g setdash\n", pr, o.Dots(DPI))
}
}
func (e *Canvas) SetColor(c color.Color) {
if c == nil {
c = color.Black
}
if e.context().color != c {
e.context().color = c
r, g, b, _ := c.RGBA()
mx := float64(math.MaxUint16)
fmt.Fprintf(e.buf, "%.*g %.*g %.*g setrgbcolor\n", pr, float64(r)/mx,
pr, float64(g)/mx, pr, float64(b)/mx)
}
}
func (e *Canvas) Rotate(r float64) {
fmt.Fprintf(e.buf, "%.*g rotate\n", pr, r*180/math.Pi)
}
func (e *Canvas) Translate(pt vg.Point) {
fmt.Fprintf(e.buf, "%.*g %.*g translate\n",
pr, pt.X.Dots(DPI), pr, pt.Y.Dots(DPI))
}
func (e *Canvas) Scale(x, y float64) {
fmt.Fprintf(e.buf, "%.*g %.*g scale\n", pr, x, pr, y)
}
func (e *Canvas) Push() {
e.stack = append(e.stack, *e.context())
e.buf.WriteString("gsave\n")
}
func (e *Canvas) Pop() {
e.stack = e.stack[:len(e.stack)-1]
e.buf.WriteString("grestore\n")
}
func (e *Canvas) Stroke(path vg.Path) {
if e.context().width <= 0 {
return
}
e.trace(path)
e.buf.WriteString("stroke\n")
}
func (e *Canvas) Fill(path vg.Path) {
e.trace(path)
e.buf.WriteString("fill\n")
}
func (e *Canvas) trace(path vg.Path) {
e.buf.WriteString("newpath\n")
for _, comp := range path {
switch comp.Type {
case vg.MoveComp:
fmt.Fprintf(e.buf, "%.*g %.*g moveto\n", pr, comp.Pos.X, pr, comp.Pos.Y)
case vg.LineComp:
fmt.Fprintf(e.buf, "%.*g %.*g lineto\n", pr, comp.Pos.X, pr, comp.Pos.Y)
case vg.ArcComp:
end := comp.Start + comp.Angle
arcOp := "arc"
if comp.Angle < 0 {
arcOp = "arcn"
}
fmt.Fprintf(e.buf, "%.*g %.*g %.*g %.*g %.*g %s\n", pr, comp.Pos.X, pr, comp.Pos.Y,
pr, comp.Radius, pr, comp.Start*180/math.Pi, pr,
end*180/math.Pi, arcOp)
case vg.CurveComp:
var p1, p2 vg.Point
switch len(comp.Control) {
case 1:
p1 = comp.Control[0]
p2 = p1
case 2:
p1 = comp.Control[0]
p2 = comp.Control[1]
default:
panic("vgeps: invalid number of control points")
}
fmt.Fprintf(e.buf, "%.*g %.*g %.*g %.*g %.*g %.*g curveto\n",
pr, p1.X, pr, p1.Y, pr, p2.X, pr, p2.Y, pr, comp.Pos.X, pr, comp.Pos.Y)
case vg.CloseComp:
e.buf.WriteString("closepath\n")
default:
panic(fmt.Sprintf("Unknown path component type: %d\n", comp.Type))
}
}
}
func (e *Canvas) FillString(fnt font.Face, pt vg.Point, str string) {
if e.context().font != fnt.Name() || e.context().fsize != fnt.Font.Size {
e.context().font = fnt.Name()
e.context().fsize = fnt.Font.Size
fmt.Fprintf(e.buf, "/%s findfont %.*g scalefont setfont\n",
fnt.Name(), pr, fnt.Font.Size)
}
fmt.Fprintf(e.buf, "%.*g %.*g moveto\n", pr, pt.X.Dots(DPI), pr, pt.Y.Dots(DPI))
fmt.Fprintf(e.buf, "(%s) show\n", str)
}
// DrawImage implements the vg.Canvas.DrawImage method.
func (c *Canvas) DrawImage(rect vg.Rectangle, img image.Image) {
// FIXME: https://github.com/gonum/plot/issues/271
panic("vgeps: DrawImage not implemented")
}
// WriteTo writes the canvas to an io.Writer.
func (e *Canvas) WriteTo(w io.Writer) (int64, error) {
b := bufio.NewWriter(w)
n, err := e.buf.WriteTo(b)
if err != nil {
return n, err
}
m, err := fmt.Fprintln(b, "showpage")
n += int64(m)
if err != nil {
return n, err
}
return n, b.Flush()
}

423
vendor/gonum.org/v1/plot/vg/vgimg/vgimg.go generated vendored Normal file
View File

@@ -0,0 +1,423 @@
// Copyright ©2015 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package vgimg implements the vg.Canvas interface using
// git.sr.ht/~sbinet/gg as a backend to output raster images.
package vgimg // import "gonum.org/v1/plot/vg/vgimg"
import (
"bufio"
"fmt"
"image"
"image/color"
"image/draw"
"image/jpeg"
"image/png"
"io"
"git.sr.ht/~sbinet/gg"
"golang.org/x/image/tiff"
"gonum.org/v1/plot/font"
"gonum.org/v1/plot/vg"
vgdraw "gonum.org/v1/plot/vg/draw"
)
func init() {
vgdraw.RegisterFormat("png", func(w, h vg.Length) vg.CanvasWriterTo {
return PngCanvas{Canvas: New(w, h)}
})
vgdraw.RegisterFormat("jpg", func(w, h vg.Length) vg.CanvasWriterTo {
return JpegCanvas{Canvas: New(w, h)}
})
vgdraw.RegisterFormat("jpeg", func(w, h vg.Length) vg.CanvasWriterTo {
return JpegCanvas{Canvas: New(w, h)}
})
vgdraw.RegisterFormat("tif", func(w, h vg.Length) vg.CanvasWriterTo {
return TiffCanvas{Canvas: New(w, h)}
})
vgdraw.RegisterFormat("tiff", func(w, h vg.Length) vg.CanvasWriterTo {
return TiffCanvas{Canvas: New(w, h)}
})
}
// Canvas implements the vg.Canvas interface,
// drawing to an image.Image using draw2d.
type Canvas struct {
ctx *gg.Context
img draw.Image
w, h vg.Length
color []color.Color
// dpi is the number of dots per inch for this canvas.
dpi int
// width is the current line width.
width vg.Length
// backgroundColor is the background color, set by
// UseBackgroundColor.
backgroundColor color.Color
}
const (
// DefaultDPI is the default dot resolution for image
// drawing in dots per inch.
DefaultDPI = 96
// DefaultWidth and DefaultHeight are the default canvas
// dimensions.
DefaultWidth = 4 * vg.Inch
DefaultHeight = 4 * vg.Inch
)
// New returns a new image canvas.
func New(w, h vg.Length) *Canvas {
return NewWith(UseWH(w, h), UseBackgroundColor(color.White))
}
// NewWith returns a new image canvas created according to the specified
// options. The currently accepted options are UseWH,
// UseDPI, UseImage, and UseImageWithContext.
// Each of the options specifies the size of the canvas (UseWH, UseImage),
// the resolution of the canvas (UseDPI), or both (useImageWithContext).
// If size or resolution are not specified, defaults are used.
// It panics if size and resolution are overspecified (i.e., too many options are
// passed).
func NewWith(o ...option) *Canvas {
c := new(Canvas)
c.backgroundColor = color.White
var g uint32
for _, opt := range o {
f := opt(c)
if g&f != 0 {
panic("incompatible options")
}
g |= f
}
if c.dpi == 0 {
c.dpi = DefaultDPI
}
if c.w == 0 { // h should also == 0.
if c.img == nil {
c.w = DefaultWidth
c.h = DefaultHeight
} else {
w := float64(c.img.Bounds().Max.X - c.img.Bounds().Min.X)
h := float64(c.img.Bounds().Max.Y - c.img.Bounds().Min.Y)
c.w = vg.Length(w/float64(c.dpi)) * vg.Inch
c.h = vg.Length(h/float64(c.dpi)) * vg.Inch
}
}
if c.img == nil {
w := c.w / vg.Inch * vg.Length(c.dpi)
h := c.h / vg.Inch * vg.Length(c.dpi)
c.img = draw.Image(image.NewRGBA(image.Rect(0, 0, int(w+0.5), int(h+0.5))))
}
if c.ctx == nil {
c.ctx = gg.NewContextForImage(c.img)
c.ctx.SetLineCapButt()
c.img = c.ctx.Image().(draw.Image)
c.ctx.InvertY()
}
draw.Draw(c.img, c.img.Bounds(), &image.Uniform{c.backgroundColor}, image.Point{}, draw.Src)
c.color = []color.Color{color.Black}
vg.Initialize(c)
return c
}
// These constants are used to ensure that the options
// used when initializing a canvas are compatible with
// each other.
const (
setsDPI uint32 = 1 << iota
setsSize
setsBackground
)
type option func(*Canvas) uint32
// UseWH specifies the width and height of the canvas.
// The size is rounded up to the nearest pixel.
func UseWH(w, h vg.Length) option {
return func(c *Canvas) uint32 {
if w <= 0 || h <= 0 {
panic("w and h must both be > 0.")
}
c.w, c.h = w, h
return setsSize
}
}
// UseDPI sets the dots per inch of a canvas. It should only be
// used as an option argument when initializing a new canvas.
func UseDPI(dpi int) option {
if dpi <= 0 {
panic("DPI must be > 0.")
}
return func(c *Canvas) uint32 {
c.dpi = dpi
return setsDPI
}
}
// UseImage specifies an image to create
// the canvas from. The
// minimum point of the given image
// should probably be 0,0.
//
// Note that a copy of the input image is performed.
// This means that modifications applied to the canvas are not reflected
// on the original image.
func UseImage(img draw.Image) option {
return func(c *Canvas) uint32 {
c.img = img
return setsSize | setsBackground
}
}
// UseImageWithContext specifies both an image
// and a graphic context to create the canvas from.
// The minimum point of the given image
// should probably be 0,0.
func UseImageWithContext(img draw.Image, ctx *gg.Context) option {
return func(c *Canvas) uint32 {
c.img = img
c.ctx = ctx
return setsSize | setsBackground
}
}
// UseBackgroundColor specifies the image background color.
// Without UseBackgroundColor, the default color is white.
func UseBackgroundColor(c color.Color) option {
return func(canvas *Canvas) uint32 {
canvas.backgroundColor = c
return setsBackground
}
}
// Image returns the image the canvas is drawing to.
//
// The dimensions of the returned image must not be modified.
func (c *Canvas) Image() draw.Image {
return c.img
}
func (c *Canvas) Size() (w, h vg.Length) {
return c.w, c.h
}
func (c *Canvas) SetLineWidth(w vg.Length) {
c.width = w
c.ctx.SetLineWidth(w.Dots(c.DPI()))
}
func (c *Canvas) SetLineDash(ds []vg.Length, offs vg.Length) {
dashes := make([]float64, len(ds))
for i, d := range ds {
dashes[i] = d.Dots(c.DPI())
}
c.ctx.SetDashOffset(offs.Dots(c.DPI()))
c.ctx.SetDash(dashes...)
}
func (c *Canvas) SetColor(clr color.Color) {
if clr == nil {
clr = color.Black
}
c.ctx.SetColor(clr)
c.color[len(c.color)-1] = clr
}
func (c *Canvas) Rotate(t float64) {
c.ctx.Rotate(t)
}
func (c *Canvas) Translate(pt vg.Point) {
c.ctx.Translate(pt.X.Dots(c.DPI()), pt.Y.Dots(c.DPI()))
}
func (c *Canvas) Scale(x, y float64) {
c.ctx.Scale(x, y)
}
func (c *Canvas) Push() {
c.color = append(c.color, c.color[len(c.color)-1])
c.ctx.Push()
}
func (c *Canvas) Pop() {
c.color = c.color[:len(c.color)-1]
c.ctx.Pop()
}
func (c *Canvas) Stroke(p vg.Path) {
if c.width <= 0 {
return
}
c.outline(p)
c.ctx.Stroke()
}
func (c *Canvas) Fill(p vg.Path) {
c.outline(p)
c.ctx.Fill()
}
func (c *Canvas) outline(p vg.Path) {
for _, comp := range p {
switch comp.Type {
case vg.MoveComp:
c.ctx.MoveTo(comp.Pos.X.Dots(c.DPI()), comp.Pos.Y.Dots(c.DPI()))
case vg.LineComp:
c.ctx.LineTo(comp.Pos.X.Dots(c.DPI()), comp.Pos.Y.Dots(c.DPI()))
case vg.ArcComp:
c.ctx.DrawArc(comp.Pos.X.Dots(c.DPI()), comp.Pos.Y.Dots(c.DPI()),
comp.Radius.Dots(c.DPI()),
comp.Start, comp.Start+comp.Angle,
)
case vg.CurveComp:
switch len(comp.Control) {
case 1:
c.ctx.QuadraticTo(
comp.Control[0].X.Dots(c.DPI()), comp.Control[0].Y.Dots(c.DPI()),
comp.Pos.X.Dots(c.DPI()), comp.Pos.Y.Dots(c.DPI()),
)
case 2:
c.ctx.CubicTo(
comp.Control[0].X.Dots(c.DPI()), comp.Control[0].Y.Dots(c.DPI()),
comp.Control[1].X.Dots(c.DPI()), comp.Control[1].Y.Dots(c.DPI()),
comp.Pos.X.Dots(c.DPI()), comp.Pos.Y.Dots(c.DPI()),
)
default:
panic("vgimg: invalid number of control points")
}
case vg.CloseComp:
c.ctx.ClosePath()
default:
panic(fmt.Sprintf("Unknown path component: %d", comp.Type))
}
}
}
// DPI returns the resolution of the receiver in pixels per inch.
func (c *Canvas) DPI() float64 {
return float64(c.dpi)
}
func (c *Canvas) FillString(font font.Face, pt vg.Point, str string) {
if font.Font.Size == 0 {
return
}
c.ctx.Push()
defer c.ctx.Pop()
face := font.FontFace(c.DPI())
defer face.Close()
c.ctx.SetFontFace(face)
x := pt.X.Dots(c.DPI())
y := pt.Y.Dots(c.DPI())
h := c.h.Dots(c.DPI())
c.ctx.InvertY()
c.ctx.DrawString(str, x, h-y)
}
// DrawImage implements the vg.Canvas.DrawImage method.
func (c *Canvas) DrawImage(rect vg.Rectangle, img image.Image) {
var (
dpi = c.DPI()
min = rect.Min
xmin = min.X.Dots(dpi)
ymin = min.Y.Dots(dpi)
rsz = rect.Size()
width = rsz.X.Dots(dpi)
height = rsz.Y.Dots(dpi)
dx = float64(img.Bounds().Dx())
dy = float64(img.Bounds().Dy())
)
c.ctx.Push()
c.ctx.Scale(1, -1)
c.ctx.Translate(xmin, -ymin-height)
c.ctx.Scale(width/dx, height/dy)
c.ctx.DrawImage(img, 0, 0)
c.ctx.Pop()
}
// WriterCounter implements the io.Writer interface, and counts
// the total number of bytes written.
type writerCounter struct {
io.Writer
n int64
}
func (w *writerCounter) Write(p []byte) (int, error) {
n, err := w.Writer.Write(p)
w.n += int64(n)
return n, err
}
// A JpegCanvas is an image canvas with a WriteTo method
// that writes a jpeg image.
type JpegCanvas struct {
*Canvas
}
// WriteTo implements the io.WriterTo interface, writing a jpeg image.
func (c JpegCanvas) WriteTo(w io.Writer) (int64, error) {
wc := writerCounter{Writer: w}
b := bufio.NewWriter(&wc)
if err := jpeg.Encode(b, c.img, nil); err != nil {
return wc.n, err
}
err := b.Flush()
return wc.n, err
}
// A PngCanvas is an image canvas with a WriteTo method that
// writes a png image.
type PngCanvas struct {
*Canvas
}
// WriteTo implements the io.WriterTo interface, writing a png image.
func (c PngCanvas) WriteTo(w io.Writer) (int64, error) {
wc := writerCounter{Writer: w}
b := bufio.NewWriter(&wc)
if err := png.Encode(b, c.img); err != nil {
return wc.n, err
}
err := b.Flush()
return wc.n, err
}
// A TiffCanvas is an image canvas with a WriteTo method that
// writes a tiff image.
type TiffCanvas struct {
*Canvas
}
// WriteTo implements the io.WriterTo interface, writing a tiff image.
func (c TiffCanvas) WriteTo(w io.Writer) (int64, error) {
wc := writerCounter{Writer: w}
b := bufio.NewWriter(&wc)
if err := tiff.Encode(b, c.img, nil); err != nil {
return wc.n, err
}
err := b.Flush()
return wc.n, err
}

251
vendor/gonum.org/v1/plot/vg/vgpdf/cp1252.map generated vendored Normal file
View File

@@ -0,0 +1,251 @@
!00 U+0000 .notdef
!01 U+0001 .notdef
!02 U+0002 .notdef
!03 U+0003 .notdef
!04 U+0004 .notdef
!05 U+0005 .notdef
!06 U+0006 .notdef
!07 U+0007 .notdef
!08 U+0008 .notdef
!09 U+0009 .notdef
!0A U+000A .notdef
!0B U+000B .notdef
!0C U+000C .notdef
!0D U+000D .notdef
!0E U+000E .notdef
!0F U+000F .notdef
!10 U+0010 .notdef
!11 U+0011 .notdef
!12 U+0012 .notdef
!13 U+0013 .notdef
!14 U+0014 .notdef
!15 U+0015 .notdef
!16 U+0016 .notdef
!17 U+0017 .notdef
!18 U+0018 .notdef
!19 U+0019 .notdef
!1A U+001A .notdef
!1B U+001B .notdef
!1C U+001C .notdef
!1D U+001D .notdef
!1E U+001E .notdef
!1F U+001F .notdef
!20 U+0020 space
!21 U+0021 exclam
!22 U+0022 quotedbl
!23 U+0023 numbersign
!24 U+0024 dollar
!25 U+0025 percent
!26 U+0026 ampersand
!27 U+0027 quotesingle
!28 U+0028 parenleft
!29 U+0029 parenright
!2A U+002A asterisk
!2B U+002B plus
!2C U+002C comma
!2D U+002D hyphen
!2E U+002E period
!2F U+002F slash
!30 U+0030 zero
!31 U+0031 one
!32 U+0032 two
!33 U+0033 three
!34 U+0034 four
!35 U+0035 five
!36 U+0036 six
!37 U+0037 seven
!38 U+0038 eight
!39 U+0039 nine
!3A U+003A colon
!3B U+003B semicolon
!3C U+003C less
!3D U+003D equal
!3E U+003E greater
!3F U+003F question
!40 U+0040 at
!41 U+0041 A
!42 U+0042 B
!43 U+0043 C
!44 U+0044 D
!45 U+0045 E
!46 U+0046 F
!47 U+0047 G
!48 U+0048 H
!49 U+0049 I
!4A U+004A J
!4B U+004B K
!4C U+004C L
!4D U+004D M
!4E U+004E N
!4F U+004F O
!50 U+0050 P
!51 U+0051 Q
!52 U+0052 R
!53 U+0053 S
!54 U+0054 T
!55 U+0055 U
!56 U+0056 V
!57 U+0057 W
!58 U+0058 X
!59 U+0059 Y
!5A U+005A Z
!5B U+005B bracketleft
!5C U+005C backslash
!5D U+005D bracketright
!5E U+005E asciicircum
!5F U+005F underscore
!60 U+0060 grave
!61 U+0061 a
!62 U+0062 b
!63 U+0063 c
!64 U+0064 d
!65 U+0065 e
!66 U+0066 f
!67 U+0067 g
!68 U+0068 h
!69 U+0069 i
!6A U+006A j
!6B U+006B k
!6C U+006C l
!6D U+006D m
!6E U+006E n
!6F U+006F o
!70 U+0070 p
!71 U+0071 q
!72 U+0072 r
!73 U+0073 s
!74 U+0074 t
!75 U+0075 u
!76 U+0076 v
!77 U+0077 w
!78 U+0078 x
!79 U+0079 y
!7A U+007A z
!7B U+007B braceleft
!7C U+007C bar
!7D U+007D braceright
!7E U+007E asciitilde
!7F U+007F .notdef
!80 U+20AC Euro
!82 U+201A quotesinglbase
!83 U+0192 florin
!84 U+201E quotedblbase
!85 U+2026 ellipsis
!86 U+2020 dagger
!87 U+2021 daggerdbl
!88 U+02C6 circumflex
!89 U+2030 perthousand
!8A U+0160 Scaron
!8B U+2039 guilsinglleft
!8C U+0152 OE
!8E U+017D Zcaron
!91 U+2018 quoteleft
!92 U+2019 quoteright
!93 U+201C quotedblleft
!94 U+201D quotedblright
!95 U+2022 bullet
!96 U+2013 endash
!97 U+2014 emdash
!98 U+02DC tilde
!99 U+2122 trademark
!9A U+0161 scaron
!9B U+203A guilsinglright
!9C U+0153 oe
!9E U+017E zcaron
!9F U+0178 Ydieresis
!A0 U+00A0 space
!A1 U+00A1 exclamdown
!A2 U+00A2 cent
!A3 U+00A3 sterling
!A4 U+00A4 currency
!A5 U+00A5 yen
!A6 U+00A6 brokenbar
!A7 U+00A7 section
!A8 U+00A8 dieresis
!A9 U+00A9 copyright
!AA U+00AA ordfeminine
!AB U+00AB guillemotleft
!AC U+00AC logicalnot
!AD U+00AD hyphen
!AE U+00AE registered
!AF U+00AF macron
!B0 U+00B0 degree
!B1 U+00B1 plusminus
!B2 U+00B2 twosuperior
!B3 U+00B3 threesuperior
!B4 U+00B4 acute
!B5 U+00B5 mu
!B6 U+00B6 paragraph
!B7 U+00B7 periodcentered
!B8 U+00B8 cedilla
!B9 U+00B9 onesuperior
!BA U+00BA ordmasculine
!BB U+00BB guillemotright
!BC U+00BC onequarter
!BD U+00BD onehalf
!BE U+00BE threequarters
!BF U+00BF questiondown
!C0 U+00C0 Agrave
!C1 U+00C1 Aacute
!C2 U+00C2 Acircumflex
!C3 U+00C3 Atilde
!C4 U+00C4 Adieresis
!C5 U+00C5 Aring
!C6 U+00C6 AE
!C7 U+00C7 Ccedilla
!C8 U+00C8 Egrave
!C9 U+00C9 Eacute
!CA U+00CA Ecircumflex
!CB U+00CB Edieresis
!CC U+00CC Igrave
!CD U+00CD Iacute
!CE U+00CE Icircumflex
!CF U+00CF Idieresis
!D0 U+00D0 Eth
!D1 U+00D1 Ntilde
!D2 U+00D2 Ograve
!D3 U+00D3 Oacute
!D4 U+00D4 Ocircumflex
!D5 U+00D5 Otilde
!D6 U+00D6 Odieresis
!D7 U+00D7 multiply
!D8 U+00D8 Oslash
!D9 U+00D9 Ugrave
!DA U+00DA Uacute
!DB U+00DB Ucircumflex
!DC U+00DC Udieresis
!DD U+00DD Yacute
!DE U+00DE Thorn
!DF U+00DF germandbls
!E0 U+00E0 agrave
!E1 U+00E1 aacute
!E2 U+00E2 acircumflex
!E3 U+00E3 atilde
!E4 U+00E4 adieresis
!E5 U+00E5 aring
!E6 U+00E6 ae
!E7 U+00E7 ccedilla
!E8 U+00E8 egrave
!E9 U+00E9 eacute
!EA U+00EA ecircumflex
!EB U+00EB edieresis
!EC U+00EC igrave
!ED U+00ED iacute
!EE U+00EE icircumflex
!EF U+00EF idieresis
!F0 U+00F0 eth
!F1 U+00F1 ntilde
!F2 U+00F2 ograve
!F3 U+00F3 oacute
!F4 U+00F4 ocircumflex
!F5 U+00F5 otilde
!F6 U+00F6 odieresis
!F7 U+00F7 divide
!F8 U+00F8 oslash
!F9 U+00F9 ugrave
!FA U+00FA uacute
!FB U+00FB ucircumflex
!FC U+00FC udieresis
!FD U+00FD yacute
!FE U+00FE thorn
!FF U+00FF ydieresis

490
vendor/gonum.org/v1/plot/vg/vgpdf/vgpdf.go generated vendored Normal file
View File

@@ -0,0 +1,490 @@
// Copyright ©2015 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package vgpdf implements the vg.Canvas interface
// using gofpdf (github.com/phpdave11/gofpdf).
package vgpdf // import "gonum.org/v1/plot/vg/vgpdf"
import (
"bufio"
"bytes"
_ "embed"
"fmt"
"image"
"image/color"
"image/png"
"io"
"log"
"math"
"os"
"path/filepath"
"sync"
pdf "github.com/go-pdf/fpdf"
stdfnt "golang.org/x/image/font"
"gonum.org/v1/plot/font"
"gonum.org/v1/plot/vg"
"gonum.org/v1/plot/vg/draw"
)
// codePageEncoding holds informations about the characters encoding of TrueType
// font files, needed by gofpdf to embed fonts in a PDF document.
// We use cp1252 (code page 1252, Windows Western) to encode characters.
// See:
// - https://en.wikipedia.org/wiki/Windows-1252
//
// TODO: provide a Canvas-level func option to embed fonts with a user provided
// code page schema?
//
//go:embed cp1252.map
var codePageEncoding []byte
func init() {
draw.RegisterFormat("pdf", func(w, h vg.Length) vg.CanvasWriterTo {
return New(w, h)
})
}
// DPI is the nominal resolution of drawing in PDF.
const DPI = 72
// Canvas implements the vg.Canvas interface,
// drawing to a PDF.
type Canvas struct {
doc *pdf.Fpdf
w, h vg.Length
dpi int
numImages int
stack []context
fonts map[font.Font]struct{}
// Switch to embed fonts in PDF file.
// The default is to embed fonts.
// This makes the PDF file more portable but also larger.
embed bool
}
type context struct {
fill color.Color
line color.Color
width vg.Length
}
// New creates a new PDF Canvas.
func New(w, h vg.Length) *Canvas {
cfg := pdf.InitType{
UnitStr: "pt",
Size: pdf.SizeType{Wd: w.Points(), Ht: h.Points()},
}
c := &Canvas{
doc: pdf.NewCustom(&cfg),
w: w,
h: h,
dpi: DPI,
stack: make([]context, 1),
fonts: make(map[font.Font]struct{}),
embed: true,
}
c.NextPage()
vg.Initialize(c)
return c
}
// EmbedFonts specifies whether the resulting PDF canvas should
// embed the fonts or not.
// EmbedFonts returns the previous value before modification.
func (c *Canvas) EmbedFonts(v bool) bool {
prev := c.embed
c.embed = v
return prev
}
func (c *Canvas) DPI() float64 {
return float64(c.dpi)
}
func (c *Canvas) context() *context {
return &c.stack[len(c.stack)-1]
}
func (c *Canvas) Size() (w, h vg.Length) {
return c.w, c.h
}
func (c *Canvas) SetLineWidth(w vg.Length) {
c.context().width = w
lw := c.unit(w)
c.doc.SetLineWidth(lw)
}
func (c *Canvas) SetLineDash(dashes []vg.Length, offs vg.Length) {
ds := make([]float64, len(dashes))
for i, d := range dashes {
ds[i] = c.unit(d)
}
c.doc.SetDashPattern(ds, c.unit(offs))
}
func (c *Canvas) SetColor(clr color.Color) {
if clr == nil {
clr = color.Black
}
c.context().line = clr
c.context().fill = clr
r, g, b, a := rgba(clr)
c.doc.SetFillColor(r, g, b)
c.doc.SetDrawColor(r, g, b)
c.doc.SetTextColor(r, g, b)
c.doc.SetAlpha(a, "Normal")
}
func (c *Canvas) Rotate(r float64) {
c.doc.TransformRotate(-r*180/math.Pi, 0, 0)
}
func (c *Canvas) Translate(pt vg.Point) {
xp, yp := c.pdfPoint(pt)
c.doc.TransformTranslate(xp, yp)
}
func (c *Canvas) Scale(x float64, y float64) {
c.doc.TransformScale(x*100, y*100, 0, 0)
}
func (c *Canvas) Push() {
c.stack = append(c.stack, *c.context())
c.doc.TransformBegin()
}
func (c *Canvas) Pop() {
c.doc.TransformEnd()
c.stack = c.stack[:len(c.stack)-1]
}
func (c *Canvas) Stroke(p vg.Path) {
if c.context().width > 0 {
c.pdfPath(p, "D")
}
}
func (c *Canvas) Fill(p vg.Path) {
c.pdfPath(p, "F")
}
func (c *Canvas) FillString(fnt font.Face, pt vg.Point, str string) {
if fnt.Font.Size == 0 {
return
}
c.font(fnt, pt)
style := ""
if fnt.Font.Weight == stdfnt.WeightBold {
style += "B"
}
if fnt.Font.Style == stdfnt.StyleItalic {
style += "I"
}
c.doc.SetFont(fnt.Name(), style, c.unit(fnt.Font.Size))
c.Push()
defer c.Pop()
c.Translate(pt)
// go-fpdf uses the top left corner as origin.
c.Scale(1, -1)
left, top, right, bottom := c.sbounds(fnt, str)
w := right - left
h := bottom - top
margin := c.doc.GetCellMargin()
c.doc.MoveTo(-left-margin, top)
c.doc.CellFormat(w, h, str, "", 0, "BL", false, 0, "")
}
func (c *Canvas) sbounds(fnt font.Face, txt string) (left, top, right, bottom float64) {
_, h := c.doc.GetFontSize()
style := ""
if fnt.Font.Weight == stdfnt.WeightBold {
style += "B"
}
if fnt.Font.Style == stdfnt.StyleItalic {
style += "I"
}
d := c.doc.GetFontDesc(fnt.Name(), style)
if d.Ascent == 0 {
// not defined (standard font?), use average of 81%
top = 0.81 * h
} else {
top = -float64(d.Ascent) * h / float64(d.Ascent-d.Descent)
}
return 0, top, c.doc.GetStringWidth(txt), top + h
}
// DrawImage implements the vg.Canvas.DrawImage method.
func (c *Canvas) DrawImage(rect vg.Rectangle, img image.Image) {
opts := pdf.ImageOptions{ImageType: "png", ReadDpi: true}
name := c.imageName()
buf := new(bytes.Buffer)
err := png.Encode(buf, img)
if err != nil {
log.Panicf("error encoding image to PNG: %v", err)
}
c.doc.RegisterImageOptionsReader(name, opts, buf)
xp, yp := c.pdfPoint(rect.Min)
wp, hp := c.pdfPoint(rect.Size())
c.doc.ImageOptions(name, xp, yp, wp, hp, false, opts, 0, "")
}
// font registers a font and a size with the PDF canvas.
func (c *Canvas) font(fnt font.Face, pt vg.Point) {
if _, ok := c.fonts[fnt.Font]; ok {
return
}
name := fnt.Name()
key := fontKey{font: fnt, embed: c.embed}
raw := new(bytes.Buffer)
_, err := fnt.Face.WriteSourceTo(nil, raw)
if err != nil {
log.Panicf("vgpdf: could not generate font %q data for PDF: %+v", name, err)
}
zdata, jdata, err := getFont(key, raw.Bytes(), codePageEncoding)
if err != nil {
log.Panicf("vgpdf: could not generate font data for PDF: %v", err)
}
c.fonts[fnt.Font] = struct{}{}
c.doc.AddFontFromBytes(name, "", jdata, zdata)
}
// pdfPath processes a vg.Path and applies it to the canvas.
func (c *Canvas) pdfPath(path vg.Path, style string) {
var (
xp float64
yp float64
)
for _, comp := range path {
switch comp.Type {
case vg.MoveComp:
xp, yp = c.pdfPoint(comp.Pos)
c.doc.MoveTo(xp, yp)
case vg.LineComp:
c.doc.LineTo(c.pdfPoint(comp.Pos))
case vg.ArcComp:
c.arc(comp, style)
case vg.CurveComp:
px, py := c.pdfPoint(comp.Pos)
switch len(comp.Control) {
case 1:
cx, cy := c.pdfPoint(comp.Control[0])
c.doc.CurveTo(cx, cy, px, py)
case 2:
cx, cy := c.pdfPoint(comp.Control[0])
dx, dy := c.pdfPoint(comp.Control[1])
c.doc.CurveBezierCubicTo(cx, cy, dx, dy, px, py)
default:
panic("vgpdf: invalid number of control points")
}
case vg.CloseComp:
c.doc.LineTo(xp, yp)
c.doc.ClosePath()
default:
panic(fmt.Sprintf("Unknown path component type: %d\n", comp.Type))
}
}
c.doc.DrawPath(style)
}
func (c *Canvas) arc(comp vg.PathComp, style string) {
x0 := comp.Pos.X + comp.Radius*vg.Length(math.Cos(comp.Start))
y0 := comp.Pos.Y + comp.Radius*vg.Length(math.Sin(comp.Start))
c.doc.LineTo(c.pdfPointXY(x0, y0))
r := c.unit(comp.Radius)
const deg = 180 / math.Pi
angle := comp.Angle * deg
beg := comp.Start * deg
end := beg + angle
x := c.unit(comp.Pos.X)
y := c.unit(comp.Pos.Y)
c.doc.Arc(x, y, r, r, angle, beg, end, style)
x1 := comp.Pos.X + comp.Radius*vg.Length(math.Cos(comp.Start+comp.Angle))
y1 := comp.Pos.Y + comp.Radius*vg.Length(math.Sin(comp.Start+comp.Angle))
c.doc.MoveTo(c.pdfPointXY(x1, y1))
}
func (c *Canvas) pdfPointXY(x, y vg.Length) (float64, float64) {
return c.unit(x), c.unit(y)
}
func (c *Canvas) pdfPoint(pt vg.Point) (float64, float64) {
return c.unit(pt.X), c.unit(pt.Y)
}
// unit returns a fpdf.Unit, converted from a vg.Length.
func (c *Canvas) unit(l vg.Length) float64 {
return l.Dots(c.DPI())
}
// imageName generates a unique image name for this PDF canvas
func (c *Canvas) imageName() string {
c.numImages++
return fmt.Sprintf("image_%03d.png", c.numImages)
}
// WriterCounter implements the io.Writer interface, and counts
// the total number of bytes written.
type writerCounter struct {
io.Writer
n int64
}
func (w *writerCounter) Write(p []byte) (int, error) {
n, err := w.Writer.Write(p)
w.n += int64(n)
return n, err
}
// WriteTo writes the Canvas to an io.Writer.
// After calling Write, the canvas is closed
// and may no longer be used for drawing.
func (c *Canvas) WriteTo(w io.Writer) (int64, error) {
c.Pop()
c.doc.Close()
wc := writerCounter{Writer: w}
b := bufio.NewWriter(&wc)
if err := c.doc.Output(b); err != nil {
return wc.n, err
}
err := b.Flush()
return wc.n, err
}
// rgba converts a Go color into a gofpdf 3-tuple int + 1 float64
func rgba(c color.Color) (int, int, int, float64) {
if c == nil {
c = color.Black
}
r, g, b, a := c.RGBA()
return int(r >> 8), int(g >> 8), int(b >> 8), float64(a) / math.MaxUint16
}
type fontsCache struct {
sync.RWMutex
cache map[fontKey]fontVal
}
// fontKey represents a PDF font request.
// fontKey needs to know whether the font will be embedded or not,
// as gofpdf.MakeFont will generate different informations.
type fontKey struct {
font font.Face
embed bool
}
type fontVal struct {
z, j []byte
}
func (c *fontsCache) get(key fontKey) (fontVal, bool) {
c.RLock()
defer c.RUnlock()
v, ok := c.cache[key]
return v, ok
}
func (c *fontsCache) add(k fontKey, v fontVal) {
c.Lock()
defer c.Unlock()
c.cache[k] = v
}
var pdfFonts = &fontsCache{
cache: make(map[fontKey]fontVal),
}
func getFont(key fontKey, font, encoding []byte) (z, j []byte, err error) {
if v, ok := pdfFonts.get(key); ok {
return v.z, v.j, nil
}
v, err := makeFont(key, font, encoding)
if err != nil {
return nil, nil, err
}
return v.z, v.j, nil
}
func makeFont(key fontKey, font, encoding []byte) (val fontVal, err error) {
tmpdir, err := os.MkdirTemp("", "gofpdf-makefont-")
if err != nil {
return val, err
}
defer os.RemoveAll(tmpdir)
indir := filepath.Join(tmpdir, "input")
err = os.Mkdir(indir, 0755)
if err != nil {
return val, err
}
outdir := filepath.Join(tmpdir, "output")
err = os.Mkdir(outdir, 0755)
if err != nil {
return val, err
}
fname := filepath.Join(indir, "font.ttf")
encname := filepath.Join(indir, "cp1252.map")
err = os.WriteFile(fname, font, 0644)
if err != nil {
return val, err
}
err = os.WriteFile(encname, encoding, 0644)
if err != nil {
return val, err
}
err = pdf.MakeFont(fname, encname, outdir, io.Discard, key.embed)
if err != nil {
return val, err
}
if key.embed {
z, err := os.ReadFile(filepath.Join(outdir, "font.z"))
if err != nil {
return val, err
}
val.z = z
}
j, err := os.ReadFile(filepath.Join(outdir, "font.json"))
if err != nil {
return val, err
}
val.j = j
pdfFonts.add(key, val)
return val, nil
}
// NextPage creates a new page in the final PDF document.
// The new page is the new current page.
// Modifications applied to the canvas will only be applied to that new page.
func (c *Canvas) NextPage() {
if c.doc.PageNo() > 0 {
c.Pop()
}
c.doc.SetMargins(0, 0, 0)
c.doc.AddPage()
c.Push()
c.Translate(vg.Point{X: 0, Y: c.h})
c.Scale(1, -1)
}

650
vendor/gonum.org/v1/plot/vg/vgsvg/vgsvg.go generated vendored Normal file
View File

@@ -0,0 +1,650 @@
// Copyright ©2015 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package vgsvg uses svgo (github.com/ajstarks/svgo)
// as a backend for vg.
//
// By default, gonum/plot uses the Liberation fonts.
// When embedding was not requested during plot creation, it may happen that
// the generated SVG plot may not display well if the Liberation fonts are not
// available to the program displaying the SVG plot.
// See gonum.org/v1/plot/vg/vgsvg#Example_standardFonts for how to work around
// this issue.
//
// Alternatively, users may want to install the Liberation fonts on their system:
// - https://en.wikipedia.org/wiki/Liberation_fonts
package vgsvg // import "gonum.org/v1/plot/vg/vgsvg"
import (
"bufio"
"bytes"
"encoding/base64"
"fmt"
"html"
"image"
"image/color"
"image/png"
"io"
"math"
"strings"
svgo "github.com/ajstarks/svgo"
xfnt "golang.org/x/image/font"
"golang.org/x/image/font/sfnt"
"gonum.org/v1/plot/font"
"gonum.org/v1/plot/vg"
"gonum.org/v1/plot/vg/draw"
)
func init() {
draw.RegisterFormat("svg", func(w, h vg.Length) vg.CanvasWriterTo {
return New(w, h)
})
}
// pr is the precision to use when outputting float64s.
const pr = 5
const (
// DefaultWidth and DefaultHeight are the default canvas
// dimensions.
DefaultWidth = 4 * vg.Inch
DefaultHeight = 4 * vg.Inch
)
// Canvas implements the vg.Canvas interface, drawing to a SVG document.
//
// By default, fonts used by the canvas are not embedded in the produced
// SVG document. This results in smaller but less portable SVG plots.
// Users wanting completely portable SVG documents should create SVG canvases
// with the EmbedFonts function.
type Canvas struct {
svg *svgo.SVG
w, h vg.Length
hdr *bytes.Buffer // hdr is the SVG prelude, it may contain embedded fonts.
buf *bytes.Buffer // buf is the SVG document.
stack []context
// Switch to embed fonts in SVG file.
// The default is to *not* embed fonts.
// Embedding fonts makes the SVG file larger but also more portable.
embed bool
fonts map[string]struct{} // set of already embedded fonts
}
type context struct {
color color.Color
dashArray []vg.Length
dashOffset vg.Length
lineWidth vg.Length
gEnds int
}
type option func(*Canvas)
// UseWH specifies the width and height of the canvas.
func UseWH(w, h vg.Length) option {
return func(c *Canvas) {
if w <= 0 || h <= 0 {
panic("vgsvg: w and h must both be > 0")
}
c.w = w
c.h = h
}
}
// EmbedFonts specifies whether fonts should be embedded inside
// the SVG canvas.
func EmbedFonts(v bool) option {
return func(c *Canvas) {
c.embed = v
}
}
// New returns a new image canvas.
func New(w, h vg.Length) *Canvas {
return NewWith(UseWH(w, h))
}
// NewWith returns a new image canvas created according to the specified
// options. The currently accepted options is UseWH. If size is not
// specified, the default is used.
func NewWith(opts ...option) *Canvas {
buf := new(bytes.Buffer)
c := &Canvas{
svg: svgo.New(buf),
w: DefaultWidth,
h: DefaultHeight,
hdr: new(bytes.Buffer),
buf: buf,
stack: []context{{}},
embed: false,
fonts: make(map[string]struct{}),
}
for _, opt := range opts {
opt(c)
}
// This is like svg.Start, except it uses floats
// and specifies the units.
fmt.Fprintf(c.hdr, `<?xml version="1.0"?>
<!-- Generated by SVGo and Plotinum VG -->
<svg width="%.*gpt" height="%.*gpt" viewBox="0 0 %.*g %.*g"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">`+"\n",
pr, c.w,
pr, c.h,
pr, c.w,
pr, c.h,
)
if c.embed {
fmt.Fprintf(c.hdr, "<defs>\n\t<style>\n")
}
// Swap the origin to the bottom left.
// This must be matched with a </g> when saving,
// before the closing </svg>.
c.svg.Gtransform(fmt.Sprintf("scale(1, -1) translate(0, -%.*g)", pr, c.h.Points()))
vg.Initialize(c)
return c
}
func (c *Canvas) Size() (w, h vg.Length) {
return c.w, c.h
}
func (c *Canvas) context() *context {
return &c.stack[len(c.stack)-1]
}
func (c *Canvas) SetLineWidth(w vg.Length) {
c.context().lineWidth = w
}
func (c *Canvas) SetLineDash(dashes []vg.Length, offs vg.Length) {
c.context().dashArray = dashes
c.context().dashOffset = offs
}
func (c *Canvas) SetColor(clr color.Color) {
c.context().color = clr
}
func (c *Canvas) Rotate(rot float64) {
rot = rot * 180 / math.Pi
c.svg.Rotate(rot)
c.context().gEnds++
}
func (c *Canvas) Translate(pt vg.Point) {
c.svg.Gtransform(fmt.Sprintf("translate(%.*g, %.*g)", pr, pt.X.Points(), pr, pt.Y.Points()))
c.context().gEnds++
}
func (c *Canvas) Scale(x, y float64) {
c.svg.ScaleXY(x, y)
c.context().gEnds++
}
func (c *Canvas) Push() {
top := *c.context()
top.gEnds = 0
c.stack = append(c.stack, top)
}
func (c *Canvas) Pop() {
for i := 0; i < c.context().gEnds; i++ {
c.svg.Gend()
}
c.stack = c.stack[:len(c.stack)-1]
}
func (c *Canvas) Stroke(path vg.Path) {
if c.context().lineWidth.Points() <= 0 {
return
}
c.svg.Path(c.pathData(path),
style(elm("fill", "#000000", "none"),
elm("stroke", "none", colorString(c.context().color)),
elm("stroke-opacity", "1", opacityString(c.context().color)),
elm("stroke-width", "1", "%.*g", pr, c.context().lineWidth.Points()),
elm("stroke-dasharray", "none", dashArrayString(c)),
elm("stroke-dashoffset", "0", "%.*g", pr, c.context().dashOffset.Points())))
}
func (c *Canvas) Fill(path vg.Path) {
c.svg.Path(c.pathData(path),
style(elm("fill", "#000000", colorString(c.context().color)),
elm("fill-opacity", "1", opacityString(c.context().color))))
}
func (c *Canvas) pathData(path vg.Path) string {
buf := new(bytes.Buffer)
var x, y float64
for _, comp := range path {
switch comp.Type {
case vg.MoveComp:
fmt.Fprintf(buf, "M%.*g,%.*g", pr, comp.Pos.X.Points(), pr, comp.Pos.Y.Points())
x = comp.Pos.X.Points()
y = comp.Pos.Y.Points()
case vg.LineComp:
fmt.Fprintf(buf, "L%.*g,%.*g", pr, comp.Pos.X.Points(), pr, comp.Pos.Y.Points())
x = comp.Pos.X.Points()
y = comp.Pos.Y.Points()
case vg.ArcComp:
r := comp.Radius.Points()
sin, cos := math.Sincos(comp.Start)
x0 := comp.Pos.X.Points() + r*cos
y0 := comp.Pos.Y.Points() + r*sin
if x0 != x || y0 != y {
fmt.Fprintf(buf, "L%.*g,%.*g", pr, x0, pr, y0)
}
if math.Abs(comp.Angle) >= 2*math.Pi {
x, y = circle(buf, c, &comp)
} else {
x, y = arc(buf, c, &comp)
}
case vg.CurveComp:
switch len(comp.Control) {
case 1:
fmt.Fprintf(buf, "Q%.*g,%.*g,%.*g,%.*g",
pr, comp.Control[0].X.Points(), pr, comp.Control[0].Y.Points(),
pr, comp.Pos.X.Points(), pr, comp.Pos.Y.Points())
case 2:
fmt.Fprintf(buf, "C%.*g,%.*g,%.*g,%.*g,%.*g,%.*g",
pr, comp.Control[0].X.Points(), pr, comp.Control[0].Y.Points(),
pr, comp.Control[1].X.Points(), pr, comp.Control[1].Y.Points(),
pr, comp.Pos.X.Points(), pr, comp.Pos.Y.Points())
default:
panic("vgsvg: invalid number of control points")
}
x = comp.Pos.X.Points()
y = comp.Pos.Y.Points()
case vg.CloseComp:
buf.WriteString("Z")
default:
panic(fmt.Sprintf("vgsvg: unknown path component type: %d", comp.Type))
}
}
return buf.String()
}
// circle adds circle path data to the given writer.
// Circles must be drawn using two arcs because
// SVG disallows the start and end point of an arc
// from being at the same location.
func circle(w io.Writer, c *Canvas, comp *vg.PathComp) (x, y float64) {
angle := 2 * math.Pi
if comp.Angle < 0 {
angle = -2 * math.Pi
}
angle += remainder(comp.Angle, 2*math.Pi)
if angle >= 4*math.Pi {
panic("Impossible angle")
}
s0, c0 := math.Sincos(comp.Start + 0.5*angle)
s1, c1 := math.Sincos(comp.Start + angle)
r := comp.Radius.Points()
x0 := comp.Pos.X.Points() + r*c0
y0 := comp.Pos.Y.Points() + r*s0
x = comp.Pos.X.Points() + r*c1
y = comp.Pos.Y.Points() + r*s1
fmt.Fprintf(w, "A%.*g,%.*g 0 %d %d %.*g,%.*g", pr, r, pr, r,
large(angle/2), sweep(angle/2), pr, x0, pr, y0) //
fmt.Fprintf(w, "A%.*g,%.*g 0 %d %d %.*g,%.*g", pr, r, pr, r,
large(angle/2), sweep(angle/2), pr, x, pr, y)
return
}
// remainder returns the remainder of x/y.
// We don't use math.Remainder because it
// seems to return incorrect values due to how
// IEEE defines the remainder operation…
func remainder(x, y float64) float64 {
return (x/y - math.Trunc(x/y)) * y
}
// arc adds arc path data to the given writer.
// Arc can only be used if the arc's angle is
// less than a full circle, if it is greater then
// circle should be used instead.
func arc(w io.Writer, c *Canvas, comp *vg.PathComp) (x, y float64) {
r := comp.Radius.Points()
sin, cos := math.Sincos(comp.Start + comp.Angle)
x = comp.Pos.X.Points() + r*cos
y = comp.Pos.Y.Points() + r*sin
fmt.Fprintf(w, "A%.*g,%.*g 0 %d %d %.*g,%.*g", pr, r, pr, r,
large(comp.Angle), sweep(comp.Angle), pr, x, pr, y)
return
}
// sweep returns the arc sweep flag value for
// the given angle.
func sweep(a float64) int {
if a < 0 {
return 0
}
return 1
}
// large returns the arc's large flag value for
// the given angle.
func large(a float64) int {
if math.Abs(a) >= math.Pi {
return 1
}
return 0
}
// FillString draws str at position pt using the specified font.
// Text passed to FillString is escaped with html.EscapeString.
func (c *Canvas) FillString(font font.Face, pt vg.Point, str string) {
name := svgFontDescr(font)
sty := style(
name,
elm("font-size", "medium", "%.*gpx", pr, font.Font.Size.Points()),
elm("fill", "#000000", colorString(c.context().color)),
)
if sty != "" {
sty = "\n\t" + sty
}
fmt.Fprintf(
c.buf,
`<text x="%.*g" y="%.*g" transform="scale(1, -1)"%s>%s</text>`+"\n",
pr, pt.X.Points(), pr, -pt.Y.Points(), sty, html.EscapeString(str),
)
if c.embed {
c.embedFont(name, font)
}
}
// DrawImage implements the vg.Canvas.DrawImage method.
func (c *Canvas) DrawImage(rect vg.Rectangle, img image.Image) {
buf := new(bytes.Buffer)
err := png.Encode(buf, img)
if err != nil {
panic(fmt.Errorf("vgsvg: error encoding image to PNG: %+v", err))
}
str := "data:image/jpg;base64," + base64.StdEncoding.EncodeToString(buf.Bytes())
rsz := rect.Size()
min := rect.Min
var (
width = rsz.X.Points()
height = rsz.Y.Points()
xmin = min.X.Points()
ymin = min.Y.Points()
)
fmt.Fprintf(
c.buf,
`<image x="%v" y="%v" width="%v" height="%v" xlink:href="%s" %s />`+"\n",
xmin,
-ymin-height,
width,
height,
str,
// invert y so image is not upside-down
`transform="scale(1, -1)"`,
)
}
// svgFontDescr returns a SVG compliant font name from the provided font face.
func svgFontDescr(fnt font.Face) string {
var (
family = svgFamilyName(fnt)
variant = svgVariantName(fnt.Font.Variant)
style = svgStyleName(fnt.Font.Style)
weight = svgWeightName(fnt.Font.Weight)
)
o := "font-family:" + family + ";" +
"font-variant:" + variant + ";" +
"font-weight:" + weight + ";" +
"font-style:" + style
return o
}
func svgFamilyName(fnt font.Face) string {
// https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/font-family
var buf sfnt.Buffer
name, err := fnt.Face.Name(&buf, sfnt.NameIDFamily)
if err != nil {
// this should never happen unless the underlying sfnt.Font data
// is somehow corrupted.
panic(fmt.Errorf(
"vgsvg: could not extract family name from font %q: %+v",
fnt.Font.Typeface,
err,
))
}
return name
}
func svgVariantName(v font.Variant) string {
// https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/font-variant
str := strings.ToLower(string(v))
switch str {
case "smallcaps":
return "small-caps"
case "mono", "monospace",
"sans", "sansserif", "sans-serif",
"serif":
// handle mismatch between the meaning of gonum/plot/font.Font#Variant
// and SVG's meaning for font-variant.
// For SVG, mono, ... serif is encoded in the font-family attribute
// whereas for gonum/plot it describes a variant among a collection of fonts.
//
// It shouldn't matter much if an invalid font-variant value is written
// out (browsers will just ignore it; Firefox 98 and Chromium 91 do so.)
return "normal"
case "":
return "none"
default:
return str
}
}
func svgStyleName(sty xfnt.Style) string {
// https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/font-style
switch sty {
case xfnt.StyleNormal:
return "normal"
case xfnt.StyleItalic:
return "italic"
case xfnt.StyleOblique:
return "oblique"
default:
panic(fmt.Errorf("vgsvg: invalid font style %+v (v=%d)", sty, int(sty)))
}
}
func svgWeightName(w xfnt.Weight) string {
// see:
// https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/font-weight
// https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight
switch w {
case xfnt.WeightThin:
return "100"
case xfnt.WeightExtraLight:
return "200"
case xfnt.WeightLight:
return "300"
case xfnt.WeightNormal:
return "normal"
case xfnt.WeightMedium:
return "500"
case xfnt.WeightSemiBold:
return "600"
case xfnt.WeightBold:
return "bold"
case xfnt.WeightExtraBold:
return "800"
case xfnt.WeightBlack:
return "900"
default:
panic(fmt.Errorf("vgsvg: invalid font weight %+v (v=%d)", w, int(w)))
}
}
func (c *Canvas) embedFont(name string, f font.Face) {
if _, dup := c.fonts[name]; dup {
return
}
c.fonts[name] = struct{}{}
raw := new(bytes.Buffer)
_, err := f.Face.WriteSourceTo(nil, raw)
if err != nil {
panic(fmt.Errorf("vg/vgsvg: could not read font raw data: %+v", err))
}
fmt.Fprintf(c.hdr, "\t\t@font-face{\n")
fmt.Fprintf(c.hdr, "\t\t\tfont-family:%q;\n", svgFamilyName(f))
fmt.Fprintf(c.hdr,
"\t\t\tfont-variant:%s;font-weight:%s;font-style:%s;\n",
svgVariantName(f.Font.Variant),
svgWeightName(f.Font.Weight),
svgStyleName(f.Font.Style),
)
fmt.Fprintf(
c.hdr,
"\t\t\tsrc: url(data:font/ttf;charset=utf-8;base64,%s) format(\"truetype\");\n",
base64.StdEncoding.EncodeToString(raw.Bytes()),
)
fmt.Fprintf(c.hdr, "\t\t}\n")
}
type cwriter struct {
w *bufio.Writer
n int64
}
func (c *cwriter) Write(p []byte) (int, error) {
n, err := c.w.Write(p)
c.n += int64(n)
return n, err
}
// WriteTo writes the canvas to an io.Writer.
func (c *Canvas) WriteTo(w io.Writer) (int64, error) {
b := &cwriter{w: bufio.NewWriter(w)}
if c.embed {
fmt.Fprintf(c.hdr, "\t</style>\n</defs>\n")
}
_, err := c.hdr.WriteTo(b)
if err != nil {
return b.n, err
}
_, err = c.buf.WriteTo(b)
if err != nil {
return b.n, err
}
// Close the groups and svg in the output buffer
// so that the Canvas is not closed and can be
// used again if needed.
for i := 0; i < c.nEnds(); i++ {
_, err = fmt.Fprintln(b, "</g>")
if err != nil {
return b.n, err
}
}
_, err = fmt.Fprintln(b, "</svg>")
if err != nil {
return b.n, err
}
return b.n, b.w.Flush()
}
// nEnds returns the number of group ends
// needed before the SVG is saved.
func (c *Canvas) nEnds() int {
n := 1 // close the transform that moves the origin
for _, ctx := range c.stack {
n += ctx.gEnds
}
return n
}
// style returns a style string composed of
// all of the given elements. If the elements
// are all empty then the empty string is
// returned.
func style(elms ...string) string {
str := ""
for _, e := range elms {
if e == "" {
continue
}
if str != "" {
str += ";"
}
str += e
}
if str == "" {
return ""
}
return "style=\"" + str + "\""
}
// elm returns a style element string with the
// given key and value. If the value matches
// default then the empty string is returned.
func elm(key, def, f string, vls ...interface{}) string {
value := fmt.Sprintf(f, vls...)
if value == def {
return ""
}
return key + ":" + value
}
// dashArrayString returns a string representing the
// dash array specification.
func dashArrayString(c *Canvas) string {
str := ""
for i, d := range c.context().dashArray {
str += fmt.Sprintf("%.*g", pr, d.Points())
if i < len(c.context().dashArray)-1 {
str += ","
}
}
if str == "" {
str = "none"
}
return str
}
// colorString returns the hexadecimal string representation of the color
func colorString(clr color.Color) string {
if clr == nil {
clr = color.Black
}
r, g, b, _a := clr.RGBA()
a := 255.0 / float64(_a)
return fmt.Sprintf("#%02X%02X%02X", int(float64(r)*a),
int(float64(g)*a), int(float64(b)*a))
}
// opacityString returns the opacity value of the given color.
func opacityString(clr color.Color) string {
if clr == nil {
clr = color.Black
}
_, _, _, a := clr.RGBA()
return fmt.Sprintf("%.*g", pr, float64(a)/math.MaxUint16)
}

329
vendor/gonum.org/v1/plot/vg/vgtex/canvas.go generated vendored Normal file
View File

@@ -0,0 +1,329 @@
// Copyright ©2016 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package vgtex provides a vg.Canvas implementation for LaTeX, targeted at
// the TikZ/PGF LaTeX package: https://sourceforge.net/projects/pgf
//
// vgtex generates PGF instructions that will be interpreted and rendered by LaTeX.
// vgtex allows to put any valid LaTeX notation inside plot's strings.
package vgtex // import "gonum.org/v1/plot/vg/vgtex"
import (
"bufio"
"bytes"
"fmt"
"image"
"image/color"
"image/png"
"io"
"math"
"os"
"strings"
"time"
"gonum.org/v1/plot/font"
"gonum.org/v1/plot/vg"
"gonum.org/v1/plot/vg/draw"
)
const degPerRadian = 180 / math.Pi
const (
defaultHeader = `%%%%%% generated by gonum/plot %%%%%%
\documentclass{standalone}
\usepackage{pgf}
\begin{document}
`
defaultFooter = "\\end{document}\n"
)
func init() {
draw.RegisterFormat("tex", func(w, h vg.Length) vg.CanvasWriterTo {
return NewDocument(w, h)
})
}
// Canvas implements the vg.Canvas interface, translating drawing
// primitives from gonum/plot to PGF.
type Canvas struct {
buf *bytes.Buffer
w, h vg.Length
stack []context
// If document is true, Canvas.WriteTo will generate a standalone
// .tex file that can be fed to, e.g., pdflatex.
document bool
id int64 // id is a unique identifier for this canvas
}
type context struct {
color color.Color
dashArray []vg.Length
dashOffset vg.Length
linew vg.Length
}
// New returns a new LaTeX canvas.
func New(w, h vg.Length) *Canvas {
return newCanvas(w, h, false)
}
// NewDocument returns a new LaTeX canvas that can be readily
// compiled into a standalone document.
func NewDocument(w, h vg.Length) *Canvas {
return newCanvas(w, h, true)
}
func newCanvas(w, h vg.Length, document bool) *Canvas {
c := &Canvas{
buf: new(bytes.Buffer),
w: w,
h: h,
document: document,
id: time.Now().UnixNano(),
}
if !document {
c.wtex(`%%%% gonum/plot created for LaTeX/pgf`)
c.wtex(`%%%% you need to add:`)
c.wtex(`%%%% \usepackage{pgf}`)
c.wtex(`%%%% to your LaTeX document`)
}
c.wtex("")
c.wtex(`\begin{pgfpicture}`)
c.stack = make([]context, 1)
vg.Initialize(c)
return c
}
func (c *Canvas) context() *context {
return &c.stack[len(c.stack)-1]
}
// Size returns the width and height of the canvas.
func (c *Canvas) Size() (w, h vg.Length) {
return c.w, c.h
}
// SetLineWidth implements the vg.Canvas.SetLineWidth method.
func (c *Canvas) SetLineWidth(w vg.Length) {
c.context().linew = w
}
// SetLineDash implements the vg.Canvas.SetLineDash method.
func (c *Canvas) SetLineDash(pattern []vg.Length, offset vg.Length) {
c.context().dashArray = pattern
c.context().dashOffset = offset
}
// SetColor implements the vg.Canvas.SetColor method.
func (c *Canvas) SetColor(clr color.Color) {
c.context().color = clr
}
// Rotate implements the vg.Canvas.Rotate method.
func (c *Canvas) Rotate(rad float64) {
c.wtex(`\pgftransformrotate{%g}`, rad*degPerRadian)
}
// Translate implements the vg.Canvas.Translate method.
func (c *Canvas) Translate(pt vg.Point) {
c.wtex(`\pgftransformshift{\pgfpoint{%gpt}{%gpt}}`, pt.X, pt.Y)
}
// Scale implements the vg.Canvas.Scale method.
func (c *Canvas) Scale(x, y float64) {
c.wtex(`\pgftransformxscale{%g}`, x)
c.wtex(`\pgftransformyscale{%g}`, y)
}
// Push implements the vg.Canvas.Push method.
func (c *Canvas) Push() {
c.wtex(`\begin{pgfscope}`)
c.stack = append(c.stack, *c.context())
}
// Pop implements the vg.Canvas.Pop method.
func (c *Canvas) Pop() {
c.stack = c.stack[:len(c.stack)-1]
c.wtex(`\end{pgfscope}`)
c.wtex("")
}
// Stroke implements the vg.Canvas.Stroke method.
func (c *Canvas) Stroke(p vg.Path) {
if c.context().linew <= 0 {
return
}
c.Push()
c.wstyle()
c.wpath(p)
c.wtex(`\pgfusepath{stroke}`)
c.Pop()
}
// Fill implements the vg.Canvas.Fill method.
func (c *Canvas) Fill(p vg.Path) {
c.Push()
c.wstyle()
c.wpath(p)
c.wtex(`\pgfusepath{fill}`)
c.Pop()
}
// FillString implements the vg.Canvas.FillString method.
func (c *Canvas) FillString(f font.Face, pt vg.Point, text string) {
c.Push()
c.wcolor()
pt.X += 0.5 * f.Width(text)
c.wtex(`\pgftext[base,at={\pgfpoint{%gpt}{%gpt}}]{{\fontsize{%gpt}{%gpt}\selectfont %s}}`, pt.X, pt.Y, f.Font.Size, f.Font.Size, text)
c.Pop()
}
// DrawImage implements the vg.Canvas.DrawImage method.
// DrawImage will first save the image inside a PNG file and have the
// generated LaTeX reference that file.
// The file name will be "gonum-pgf-image-<canvas-id>-<time.Now()>.png
func (c *Canvas) DrawImage(rect vg.Rectangle, img image.Image) {
fname := fmt.Sprintf("gonum-pgf-image-%v-%v.png", c.id, time.Now().UnixNano())
f, err := os.Create(fname)
if err != nil {
panic(err)
}
defer f.Close()
err = png.Encode(f, img)
if err != nil {
panic(fmt.Errorf("vgtex: error encoding image to PNG: %v", err))
}
var (
xmin = rect.Min.X
ymin = rect.Min.Y
width = rect.Size().X
height = rect.Size().Y
)
c.wtex(`\pgftext[base,left,at=\pgfpoint{%gpt}{%gpt}]{\pgfimage[height=%gpt,width=%gpt]{%s}}`, xmin, ymin, height, width, fname)
}
func (c *Canvas) indent(s string) string {
return strings.Repeat(s, len(c.stack))
}
func (c *Canvas) wtex(s string, args ...interface{}) {
fmt.Fprintf(c.buf, c.indent(" ")+s+"\n", args...)
}
func (c *Canvas) wstyle() {
c.wdash()
c.wlineWidth()
c.wcolor()
}
func (c *Canvas) wdash() {
if len(c.context().dashArray) == 0 {
c.wtex(`\pgfsetdash{}{0pt}`)
return
}
str := `\pgfsetdash{`
for _, d := range c.context().dashArray {
str += fmt.Sprintf("{%gpt}", d)
}
str += fmt.Sprintf("}{%gpt}", c.context().dashOffset)
c.wtex(str)
}
func (c *Canvas) wlineWidth() {
c.wtex(`\pgfsetlinewidth{%gpt}`, c.context().linew)
}
func (c *Canvas) wcolor() {
col := c.context().color
if col == nil {
col = color.Black
}
r, g, b, a := col.RGBA()
// FIXME(sbinet) \color will last until the end of the current TeX group
// use \pgfsetcolor and \pgfsetstrokecolor instead.
// it needs a named color: define it on the fly (storing it at the beginning
// of the document.)
c.wtex(
`\color[rgb]{%g,%g,%g}`,
float64(r)/math.MaxUint16,
float64(g)/math.MaxUint16,
float64(b)/math.MaxUint16,
)
opacity := float64(a) / math.MaxUint16
c.wtex(`\pgfsetstrokeopacity{%g}`, opacity)
c.wtex(`\pgfsetfillopacity{%g}`, opacity)
}
func (c *Canvas) wpath(p vg.Path) {
for _, comp := range p {
switch comp.Type {
case vg.MoveComp:
c.wtex(`\pgfpathmoveto{\pgfpoint{%gpt}{%gpt}}`, comp.Pos.X, comp.Pos.Y)
case vg.LineComp:
c.wtex(`\pgflineto{\pgfpoint{%gpt}{%gpt}}`, comp.Pos.X, comp.Pos.Y)
case vg.ArcComp:
start := comp.Start * degPerRadian
angle := comp.Angle * degPerRadian
r := comp.Radius
c.wtex(`\pgfpatharc{%g}{%g}{%gpt}`, start, angle, r)
case vg.CurveComp:
var a, b vg.Point
switch len(comp.Control) {
case 1:
a = comp.Control[0]
b = a
case 2:
a = comp.Control[0]
b = comp.Control[1]
default:
panic("vgtex: invalid number of control points")
}
c.wtex(`\pgfcurveto{\pgfpoint{%gpt}{%gpt}}{\pgfpoint{%gpt}{%gpt}}{\pgfpoint{%gpt}{%gpt}}`,
a.X, a.Y, b.X, b.Y, comp.Pos.X, comp.Pos.Y)
case vg.CloseComp:
c.wtex("%% path-close")
default:
panic(fmt.Errorf("vgtex: unknown path component type: %v", comp.Type))
}
}
}
// WriteTo implements the io.WriterTo interface, writing a LaTeX/pgf plot.
func (c *Canvas) WriteTo(w io.Writer) (int64, error) {
var (
n int64
nn int
err error
)
b := bufio.NewWriter(w)
if c.document {
nn, err = b.Write([]byte(defaultHeader))
n += int64(nn)
if err != nil {
return n, err
}
}
m, err := c.buf.WriteTo(b)
n += m
if err != nil {
return n, err
}
nn, err = fmt.Fprintf(b, "\\end{pgfpicture}\n")
n += int64(nn)
if err != nil {
return n, err
}
if c.document {
nn, err = b.Write([]byte(defaultFooter))
n += int64(nn)
if err != nil {
return n, err
}
}
return n, b.Flush()
}