vendor: sync with latest

Signed-off-by: Gyuho Lee <gyuhox@gmail.com>
This commit is contained in:
Gyuho Lee 2018-05-10 10:48:15 -07:00
parent 931e864ffd
commit 75ce1a357e
17 changed files with 370 additions and 211 deletions

11
Gopkg.lock generated
View File

@ -63,7 +63,7 @@
[[projects]]
name = "github.com/dustin/go-humanize"
packages = ["."]
revision = "bb3d318650d48840a39aa21a027c6630e198e626"
revision = "02af3965c54e8cacf948b97fef38925c4120652c"
source = "https://github.com/dustin/go-humanize"
[[projects]]
@ -122,7 +122,7 @@
"schema",
"top"
]
revision = "187ae4baf4c1bed94dfeb338dfc1137cd6dfadc3"
revision = "997067d2074a699295cf069e19e745e14db9bcf3"
source = "https://github.com/gyuho/linux-inspect"
[[projects]]
@ -159,7 +159,6 @@
name = "github.com/kr/pty"
packages = ["."]
revision = "1278f20d9cf7455f0465f3bf74a73d1eeb555c0f"
source = "https://github.com/kr/pty"
[[projects]]
branch = "master"
@ -186,7 +185,7 @@
[[projects]]
name = "github.com/olekukonko/tablewriter"
packages = ["."]
revision = "96aac992fc8b1a4c83841a6c3e7178d20d989625"
revision = "d4647c9c7a84d847478d890b816b7d8b62b0b279"
source = "https://github.com/olekukonko/tablewriter"
[[projects]]
@ -315,7 +314,7 @@
"vg/vgpdf",
"vg/vgsvg"
]
revision = "feab214a240f4312b98ab52baf662b55ff1ee377"
revision = "8e90bd1840aacc281e54298e39ac0ae2be794419"
source = "https://github.com/gonum/plot"
[[projects]]
@ -395,6 +394,6 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "5f3fd3c1bb42b7e13f03ac35628c6743379babd27ebf047e5f1124fcc7f25125"
inputs-digest = "69cdc0d9c700a95cfecff8f95f783cc922720cd16666ffe7279cb702a812fa58"
solver-name = "gps-cdcl"
solver-version = 1

View File

@ -45,7 +45,7 @@
[[constraint]]
name = "github.com/dustin/go-humanize"
source = "https://github.com/dustin/go-humanize"
revision = "bb3d318650d48840a39aa21a027c6630e198e626"
revision = "02af3965c54e8cacf948b97fef38925c4120652c"
[[constraint]]
name = "github.com/spf13/cobra"
@ -55,7 +55,7 @@
[[constraint]]
name = "github.com/olekukonko/tablewriter"
source = "https://github.com/olekukonko/tablewriter"
revision = "96aac992fc8b1a4c83841a6c3e7178d20d989625"
revision = "d4647c9c7a84d847478d890b816b7d8b62b0b279"
[[constraint]]
name = "github.com/cheggaaa/pb"
@ -84,13 +84,13 @@
[[constraint]]
name = "github.com/gyuho/linux-inspect"
source = "https://github.com/gyuho/linux-inspect"
revision = "187ae4baf4c1bed94dfeb338dfc1137cd6dfadc3"
revision = "997067d2074a699295cf069e19e745e14db9bcf3"
[[constraint]]
name = "gonum.org/v1/plot"
source = "https://github.com/gonum/plot"
revision = "feab214a240f4312b98ab52baf662b55ff1ee377"
revision = "8e90bd1840aacc281e54298e39ac0ae2be794419"
[[constraint]]

View File

@ -134,7 +134,7 @@ sudo rm -rf ${HOME}/*
# GO_VERSION=1.8.3
# etcd v3.3.0
GO_VERSION=1.9.3
GO_VERSION=1.9.6
sudo rm -f /usr/local/go/bin/go && sudo rm -rf /usr/local/go && sudo rm -f /bin/go
@ -322,9 +322,9 @@ curl -X PURGE https://camo.githubusercontent.com/a6e057c6a9cff6a8d49f4c5b83a2b47
plotly
etcd v3.2.0 (Go 1.8.3)
etcd v3.3.0 (Go 1.9.3)
etcd v3.3.0 (Go 1.9.6)
Zookeeper r3.5.3-beta (Java 8)
Consul v1.0.2 (Go 1.9.3)
Consul v1.0.2 (Go 1.9.6)
Graph BoxPlot
Traces

View File

@ -17,7 +17,7 @@ cd ${GOPATH}/src/${GIT_PATH}
git reset --hard HEAD
./build
make build
${GOPATH}/src/${GIT_PATH}/bin/etcd --version
${GOPATH}/src/${GIT_PATH}/bin/etcdctl --version

View File

@ -76,6 +76,14 @@ func Commaf(v float64) string {
return buf.String()
}
// CommafWithDigits works like the Commaf but limits the resulting
// string to the given number of decimal places.
//
// e.g. CommafWithDigits(834142.32, 1) -> 834,142.3
func CommafWithDigits(f float64, decimals int) string {
return stripTrailingDigits(Commaf(f), decimals)
}
// BigComma produces a string form of the given big.Int in base 10
// with commas after every three orders of magnitude.
func BigComma(b *big.Int) string {

View File

@ -1,6 +1,9 @@
package humanize
import "strconv"
import (
"strconv"
"strings"
)
func stripTrailingZeros(s string) string {
offset := len(s) - 1
@ -17,7 +20,27 @@ func stripTrailingZeros(s string) string {
return s[:offset+1]
}
func stripTrailingDigits(s string, digits int) string {
if i := strings.Index(s, "."); i >= 0 {
if digits <= 0 {
return s[:i]
}
i++
if i+digits >= len(s) {
return s
}
return s[:i+digits]
}
return s
}
// Ftoa converts a float to a string with no trailing zeros.
func Ftoa(num float64) string {
return stripTrailingZeros(strconv.FormatFloat(num, 'f', 6, 64))
}
// FtoaWithDigits converts a float to a string but limits the resulting string
// to the given number of decimal places, and no trailing zeros.
func FtoaWithDigits(num float64, digits int) string {
return stripTrailingZeros(stripTrailingDigits(strconv.FormatFloat(num, 'f', 6, 64), digits))
}

View File

@ -93,6 +93,16 @@ func SI(input float64, unit string) string {
return Ftoa(value) + " " + prefix + unit
}
// SIWithDigits works like SI but limits the resulting string to the
// given number of decimal places.
//
// e.g. SIWithDigits(1000000, 0, "B") -> 1 MB
// e.g. SIWithDigits(2.2345e-12, 2, "F") -> 2.23 pF
func SIWithDigits(input float64, decimals int, unit string) string {
value, prefix := ComputeSI(input)
return FtoaWithDigits(value, decimals) + " " + prefix + unit
}
var errInvalid = errors.New("invalid input")
// ParseSI parses an SI string back into the number and unit.

View File

@ -53,12 +53,13 @@ type Table struct {
lines [][][]string
cs map[int]int
rs map[int]int
headers []string
footers []string
headers [][]string
footers [][]string
caption bool
captionText string
autoFmt bool
autoWrap bool
reflowText bool
mW int
pCenter string
pRow string
@ -89,12 +90,13 @@ func NewWriter(writer io.Writer) *Table {
lines: [][][]string{},
cs: make(map[int]int),
rs: make(map[int]int),
headers: []string{},
footers: []string{},
headers: [][]string{},
footers: [][]string{},
caption: false,
captionText: "Table caption.",
autoFmt: true,
autoWrap: true,
reflowText: true,
mW: MAX_ROW_WIDTH,
pCenter: CENTER,
pRow: ROW,
@ -137,12 +139,17 @@ func (t *Table) Render() {
}
}
const (
headerRowIdx = -1
footerRowIdx = -2
)
// Set table header
func (t *Table) SetHeader(keys []string) {
t.colSize = len(keys)
for i, v := range keys {
t.parseDimension(v, i, -1)
t.headers = append(t.headers, v)
lines := t.parseDimension(v, i, headerRowIdx)
t.headers = append(t.headers, lines)
}
}
@ -150,8 +157,8 @@ func (t *Table) SetHeader(keys []string) {
func (t *Table) SetFooter(keys []string) {
//t.colSize = len(keys)
for i, v := range keys {
t.parseDimension(v, i, -1)
t.footers = append(t.footers, v)
lines := t.parseDimension(v, i, footerRowIdx)
t.footers = append(t.footers, lines)
}
}
@ -173,6 +180,11 @@ func (t *Table) SetAutoWrapText(auto bool) {
t.autoWrap = auto
}
// Turn automatic reflowing of multiline text when rewrapping. Default is on (true).
func (t *Table) SetReflowDuringAutoWrap(auto bool) {
t.reflowText = auto
}
// Set the Default column width
func (t *Table) SetColWidth(width int) {
t.mW = width
@ -304,7 +316,7 @@ func (t *Table) ClearRows() {
// Clear footer
func (t *Table) ClearFooter() {
t.footers = []string{}
t.footers = [][]string{}
}
// Print line based on row width
@ -367,10 +379,6 @@ func (t *Table) printHeading() {
return
}
// Check if border is set
// Replace with space if not set
fmt.Fprint(t.out, ConditionString(t.borders.Left, t.pColumn, SPACE))
// Identify last column
end := len(t.cs) - 1
@ -383,31 +391,39 @@ func (t *Table) printHeading() {
is_esc_seq = true
}
// Print Heading column
for i := 0; i <= end; i++ {
v := t.cs[i]
h := ""
if i < len(t.headers) {
h = t.headers[i]
}
if t.autoFmt {
h = Title(h)
}
pad := ConditionString((i == end && !t.borders.Left), SPACE, t.pColumn)
// Maximum height.
max := t.rs[headerRowIdx]
if is_esc_seq {
fmt.Fprintf(t.out, " %s %s",
format(padFunc(h, SPACE, v),
t.headerParams[i]), pad)
} else {
fmt.Fprintf(t.out, " %s %s",
padFunc(h, SPACE, v),
pad)
}
// Print Heading
for x := 0; x < max; x++ {
// Check if border is set
// Replace with space if not set
fmt.Fprint(t.out, ConditionString(t.borders.Left, t.pColumn, SPACE))
for y := 0; y <= end; y++ {
v := t.cs[y]
h := ""
if y < len(t.headers) && x < len(t.headers[y]) {
h = t.headers[y][x]
}
if t.autoFmt {
h = Title(h)
}
pad := ConditionString((y == end && !t.borders.Left), SPACE, t.pColumn)
if is_esc_seq {
fmt.Fprintf(t.out, " %s %s",
format(padFunc(h, SPACE, v),
t.headerParams[y]), pad)
} else {
fmt.Fprintf(t.out, " %s %s",
padFunc(h, SPACE, v),
pad)
}
}
// Next line
fmt.Fprint(t.out, t.newLine)
}
// Next line
fmt.Fprint(t.out, t.newLine)
if t.hdrLine {
t.printLine(true)
}
@ -424,9 +440,6 @@ func (t *Table) printFooter() {
if !t.borders.Bottom {
t.printLine(true)
}
// Check if border is set
// Replace with space if not set
fmt.Fprint(t.out, ConditionString(t.borders.Bottom, t.pColumn, SPACE))
// Identify last column
end := len(t.cs) - 1
@ -440,36 +453,50 @@ func (t *Table) printFooter() {
is_esc_seq = true
}
// Print Heading column
for i := 0; i <= end; i++ {
v := t.cs[i]
f := t.footers[i]
if t.autoFmt {
f = Title(f)
}
pad := ConditionString((i == end && !t.borders.Top), SPACE, t.pColumn)
// Maximum height.
max := t.rs[footerRowIdx]
if len(t.footers[i]) == 0 {
pad = SPACE
}
// Print Footer
erasePad := make([]bool, len(t.footers))
for x := 0; x < max; x++ {
// Check if border is set
// Replace with space if not set
fmt.Fprint(t.out, ConditionString(t.borders.Bottom, t.pColumn, SPACE))
if is_esc_seq {
fmt.Fprintf(t.out, " %s %s",
format(padFunc(f, SPACE, v),
t.footerParams[i]), pad)
} else {
fmt.Fprintf(t.out, " %s %s",
padFunc(f, SPACE, v),
pad)
}
for y := 0; y <= end; y++ {
v := t.cs[y]
f := ""
if y < len(t.footers) && x < len(t.footers[y]) {
f = t.footers[y][x]
}
if t.autoFmt {
f = Title(f)
}
pad := ConditionString((y == end && !t.borders.Top), SPACE, t.pColumn)
//fmt.Fprintf(t.out, " %s %s",
// padFunc(f, SPACE, v),
// pad)
if erasePad[y] || (x == 0 && len(f) == 0) {
pad = SPACE
erasePad[y] = true
}
if is_esc_seq {
fmt.Fprintf(t.out, " %s %s",
format(padFunc(f, SPACE, v),
t.footerParams[y]), pad)
} else {
fmt.Fprintf(t.out, " %s %s",
padFunc(f, SPACE, v),
pad)
}
//fmt.Fprintf(t.out, " %s %s",
// padFunc(f, SPACE, v),
// pad)
}
// Next line
fmt.Fprint(t.out, t.newLine)
//t.printLine(true)
}
// Next line
fmt.Fprint(t.out, t.newLine)
//t.printLine(true)
hasPrinted := false
@ -477,7 +504,7 @@ func (t *Table) printFooter() {
v := t.cs[i]
pad := t.pRow
center := t.pCenter
length := len(t.footers[i])
length := len(t.footers[i][0])
if length > 0 {
hasPrinted = true
@ -505,7 +532,7 @@ func (t *Table) printFooter() {
// Change Center start position
if center == SPACE {
if i < end && len(t.footers[i+1]) != 0 {
if i < end && len(t.footers[i+1][0]) != 0 {
center = t.pCenter
}
}
@ -564,9 +591,9 @@ func (t *Table) fillAlignment(num int) {
// Print Row Information
// Adjust column alignment based on type
func (t *Table) printRow(columns [][]string, colKey int) {
func (t *Table) printRow(columns [][]string, rowIdx int) {
// Get Maximum Height
max := t.rs[colKey]
max := t.rs[rowIdx]
total := len(columns)
// TODO Fix uneven col size
@ -578,7 +605,6 @@ func (t *Table) printRow(columns [][]string, colKey int) {
//}
// Pad Each Height
// pads := []int{}
pads := []int{}
// Checking for ANSI escape sequences for columns
@ -672,9 +698,9 @@ func (t *Table) printRowsMergeCells() {
// Print Row Information to a writer and merge identical cells.
// Adjust column alignment based on type
func (t *Table) printRowMergeCells(writer io.Writer, columns [][]string, colKey int, previousLine []string) ([]string, []bool) {
func (t *Table) printRowMergeCells(writer io.Writer, columns [][]string, rowIdx int, previousLine []string) ([]string, []bool) {
// Get Maximum Height
max := t.rs[colKey]
max := t.rs[rowIdx]
total := len(columns)
// Pad Each Height
@ -749,44 +775,59 @@ func (t *Table) printRowMergeCells(writer io.Writer, columns [][]string, colKey
func (t *Table) parseDimension(str string, colKey, rowKey int) []string {
var (
raw []string
max int
raw []string
maxWidth int
)
w := DisplayWidth(str)
// Calculate Width
// Check if with is grater than maximum width
if w > t.mW {
w = t.mW
}
// Check if width exists
v, ok := t.cs[colKey]
if !ok || v < w || v == 0 {
t.cs[colKey] = w
}
if rowKey == -1 {
return raw
}
// Calculate Height
if t.autoWrap {
raw, _ = WrapString(str, t.cs[colKey])
} else {
raw = getLines(str)
}
raw = getLines(str)
maxWidth = 0
for _, line := range raw {
if w := DisplayWidth(line); w > max {
max = w
if w := DisplayWidth(line); w > maxWidth {
maxWidth = w
}
}
// Make sure the with is the same length as maximum word
// Important for cases where the width is smaller than maxu word
if max > t.cs[colKey] {
t.cs[colKey] = max
// If wrapping, ensure that all paragraphs in the cell fit in the
// specified width.
if t.autoWrap {
// If there's a maximum allowed width for wrapping, use that.
if maxWidth > t.mW {
maxWidth = t.mW
}
// In the process of doing so, we need to recompute maxWidth. This
// is because perhaps a word in the cell is longer than the
// allowed maximum width in t.mW.
newMaxWidth := maxWidth
newRaw := make([]string, 0, len(raw))
if t.reflowText {
// Make a single paragraph of everything.
raw = []string{strings.Join(raw, " ")}
}
for i, para := range raw {
paraLines, _ := WrapString(para, maxWidth)
for _, line := range paraLines {
if w := DisplayWidth(line); w > newMaxWidth {
newMaxWidth = w
}
}
if i > 0 {
newRaw = append(newRaw, " ")
}
newRaw = append(newRaw, paraLines...)
}
raw = newRaw
maxWidth = newMaxWidth
}
// Store the new known maximum width.
v, ok := t.cs[colKey]
if !ok || v < maxWidth || v == 0 {
t.cs[colKey] = maxWidth
}
// Remember the number of lines for the row printer.
h := len(raw)
v, ok = t.rs[rowKey]

View File

@ -30,12 +30,33 @@ func ConditionString(cond bool, valid, inValid string) string {
return inValid
}
func isNumOrSpace(r rune) bool {
return ('0' <= r && r <= '9') || r == ' '
}
// Format Table Header
// Replace _ , . and spaces
func Title(name string) string {
name = strings.Replace(name, "_", " ", -1)
name = strings.Replace(name, ".", " ", -1)
origLen := len(name)
rs := []rune(name)
for i, r := range rs {
switch r {
case '_':
rs[i] = ' '
case '.':
// ignore floating number 0.0
if (i != 0 && !isNumOrSpace(rs[i-1])) || (i != len(rs)-1 && !isNumOrSpace(rs[i+1])) {
rs[i] = ' '
}
}
}
name = string(rs)
name = strings.TrimSpace(name)
if len(name) == 0 && origLen > 0 {
// Keep at least one character. This is important to preserve
// empty lines in multi-line headers/footers.
name = " "
}
return strings.ToUpper(name)
}

View File

@ -95,10 +95,5 @@ func WrapWords(words []string, spc, lim, pen int) [][]string {
// getLines decomposes a multiline string into a slice of strings.
func getLines(s string) []string {
var lines []string
for _, line := range strings.Split(s, nl) {
lines = append(lines, line)
}
return lines
return strings.Split(s, nl)
}

2
vendor/gonum.org/v1/plot/align.go generated vendored
View File

@ -63,10 +63,12 @@ func Align(plots [][]*Plot, t draw.Tiles, dc draw.Canvas) [][]draw.Canvas {
for _, s := range xSpacing {
xTotalSpace += s.n + s.p
}
xTotalSpace += float64(t.PadX) * float64(len(xSpacing)-1)
var yTotalSpace float64
for _, s := range ySpacing {
yTotalSpace += s.n + s.p
}
yTotalSpace += float64(t.PadY) * float64(len(ySpacing)-1)
avgWidth := vg.Length((float64(dc.Max.X-dc.Min.X) - xTotalSpace) / float64(t.Cols))
avgHeight := vg.Length((float64(dc.Max.Y-dc.Min.Y) - yTotalSpace) / float64(t.Rows))

40
vendor/gonum.org/v1/plot/legend.go generated vendored
View File

@ -5,6 +5,8 @@
package plot
import (
"math"
"gonum.org/v1/plot/vg"
"gonum.org/v1/plot/vg/draw"
)
@ -67,9 +69,9 @@ type Thumbnailer interface {
Thumbnail(c *draw.Canvas)
}
// makeLegend returns a legend with the default
// NewLegend returns a legend with the default
// parameter settings.
func makeLegend() (Legend, error) {
func NewLegend() (Legend, error) {
font, err := vg.MakeFont(DefaultFont, vg.Points(12))
if err != nil {
return Legend{}, err
@ -80,8 +82,8 @@ func makeLegend() (Legend, error) {
}, nil
}
// draw draws the legend to the given draw.Canvas.
func (l *Legend) draw(c draw.Canvas) {
// Draw draws the legend to the given draw.Canvas.
func (l *Legend) Draw(c draw.Canvas) {
iconx := c.Min.X
sty := l.TextStyle
textx := iconx + l.ThumbnailWidth + sty.Rectangle(" ").Max.X
@ -118,6 +120,36 @@ func (l *Legend) draw(c draw.Canvas) {
}
}
// Rectangle returns the extent of the Legend.
func (l *Legend) Rectangle(c draw.Canvas) vg.Rectangle {
var width, height vg.Length
sty := l.TextStyle
entryHeight := l.entryHeight()
for i, e := range l.entries {
width = vg.Length(math.Max(float64(width), float64(l.ThumbnailWidth+sty.Rectangle(" "+e.text).Max.X)))
height += entryHeight
if i != 0 {
height += l.Padding
}
}
var r vg.Rectangle
if l.Left {
r.Max.X = c.Max.X
r.Min.X = c.Max.X - width
} else {
r.Max.X = c.Min.X + width
r.Min.X = c.Min.X
}
if l.Top {
r.Max.Y = c.Max.Y
r.Min.Y = c.Max.Y - height
} else {
r.Max.Y = c.Min.Y + height
r.Min.Y = c.Min.Y
}
return r
}
// entryHeight returns the height of the tallest legend
// entry text.
func (l *Legend) entryHeight() (height vg.Length) {

4
vendor/gonum.org/v1/plot/plot.go generated vendored
View File

@ -89,7 +89,7 @@ func New() (*Plot, error) {
if err != nil {
return nil, err
}
legend, err := makeLegend()
legend, err := NewLegend()
if err != nil {
return nil, err
}
@ -165,7 +165,7 @@ func (p *Plot) Draw(c draw.Canvas) {
data.Plot(dataC, p)
}
p.Legend.draw(draw.Crop(c, ywidth, 0, xheight, 0))
p.Legend.Draw(draw.Crop(c, ywidth, 0, xheight, 0))
}
// DataCanvas returns a new draw.Canvas that

View File

@ -9,7 +9,6 @@ import (
"gonum.org/v1/plot"
"gonum.org/v1/plot/palette"
"gonum.org/v1/plot/vg"
"gonum.org/v1/plot/vg/draw"
)
@ -56,16 +55,10 @@ func (l *ColorBar) check() {
func (l *ColorBar) Plot(c draw.Canvas, p *plot.Plot) {
l.check()
colors := l.colors(c)
var img *image.NRGBA64
var xmin, xmax, ymin, ymax vg.Length
var pImg *Image
delta := (l.ColorMap.Max() - l.ColorMap.Min()) / float64(colors)
if l.Vertical {
trX, trY := p.Transforms(&c)
xmin = trX(0)
ymin = trY(l.ColorMap.Min())
xmax = trX(1)
ymax = trY(l.ColorMap.Max())
img = image.NewNRGBA64(image.Rectangle{
img := image.NewNRGBA64(image.Rectangle{
Min: image.Point{X: 0, Y: 0},
Max: image.Point{X: 1, Y: colors},
})
@ -76,13 +69,9 @@ func (l *ColorBar) Plot(c draw.Canvas, p *plot.Plot) {
}
img.Set(0, colors-1-i, color)
}
pImg = NewImage(img, 0, l.ColorMap.Min(), 1, l.ColorMap.Max())
} else {
trX, trY := p.Transforms(&c)
xmin = trX(l.ColorMap.Min())
ymin = trY(0)
xmax = trX(l.ColorMap.Max())
ymax = trY(1)
img = image.NewNRGBA64(image.Rectangle{
img := image.NewNRGBA64(image.Rectangle{
Min: image.Point{X: 0, Y: 0},
Max: image.Point{X: colors, Y: 1},
})
@ -93,12 +82,9 @@ func (l *ColorBar) Plot(c draw.Canvas, p *plot.Plot) {
}
img.Set(i, 0, color)
}
pImg = NewImage(img, l.ColorMap.Min(), 0, l.ColorMap.Max(), 1)
}
rect := vg.Rectangle{
Min: vg.Point{X: xmin, Y: ymin},
Max: vg.Point{X: xmax, Y: ymax},
}
c.DrawImage(rect, img)
pImg.Plot(c, p)
}
// DataRange implements the DataRange method

View File

@ -56,7 +56,7 @@ func (img *Image) Plot(c draw.Canvas, p *plot.Plot) {
Min: vg.Point{X: xmin, Y: ymin},
Max: vg.Point{X: xmax, Y: ymax},
}
c.DrawImage(rect, img.img)
c.DrawImage(rect, img.transformFor(p))
}
// DataRange implements the DataRange method
@ -71,6 +71,48 @@ func (img *Image) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox {
return nil
}
// transform warps the image to align with non-linear axes.
func (img *Image) transformFor(p *plot.Plot) image.Image {
_, xLinear := p.X.Scale.(plot.LinearScale)
_, yLinear := p.Y.Scale.(plot.LinearScale)
if xLinear && yLinear {
return img.img
}
b := img.img.Bounds()
o := image.NewNRGBA64(b)
for c := 0; c < img.cols; c++ {
// Find the equivalent image column after applying axis transforms.
cTrans := int(p.X.Norm(img.x(c)) * float64(img.cols))
// Find the equivalent column of the previous image column after applying
// axis transforms.
cPrevTrans := int(p.X.Norm(img.x(maxInt(c-1, 0))) * float64(img.cols))
for r := 0; r < img.rows; r++ {
// Find the equivalent image row after applying axis transforms.
rTrans := int(p.Y.Norm(img.y(r)) * float64(img.rows))
// Find the equivalent row of the previous image row after applying
// axis transforms.
rPrevTrans := int(p.Y.Norm(img.y(maxInt(r-1, 0))) * float64(img.rows))
crColor := img.img.At(c, img.rows-r-1)
// Set all the pixels in the new image between (cPrevTrans, rPrevTrans)
// and (cTrans, rTrans) to the color at (c,r) in the original image.
// TODO: Improve interpolation.
for cPrime := cPrevTrans; cPrime <= cTrans; cPrime++ {
for rPrime := rPrevTrans; rPrime <= rTrans; rPrime++ {
o.Set(cPrime, img.rows-rPrime-1, crColor)
}
}
}
}
return o
}
func maxInt(a, b int) int {
if a > b {
return a
}
return b
}
func (img *Image) x(c int) float64 {
if c >= img.cols || c < 0 {
panic("plotter/image: illegal range")

View File

@ -23,12 +23,12 @@ import (
const DPI = 72
type Canvas struct {
stk []ctx
w, h vg.Length
buf *bytes.Buffer
stack []context
w, h vg.Length
buf *bytes.Buffer
}
type ctx struct {
type context struct {
color color.Color
width vg.Length
dashes []vg.Length
@ -48,10 +48,10 @@ func New(w, h vg.Length) *Canvas {
// NewTitle returns a new Canvas with the given title string.
func NewTitle(w, h vg.Length, title string) *Canvas {
c := &Canvas{
stk: []ctx{ctx{}},
w: w,
h: h,
buf: new(bytes.Buffer),
stack: []context{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")
@ -71,29 +71,29 @@ func (c *Canvas) Size() (w, h vg.Length) {
return c.w, c.h
}
// cur returns the top context on the stack.
func (e *Canvas) cur() *ctx {
return &e.stk[len(e.stk)-1]
// 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.cur().width != w {
e.cur().width = w
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.cur().dashes
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.cur().offs != o {
e.cur().dashes = dashes
e.cur().offs = o
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))
@ -107,8 +107,8 @@ func (e *Canvas) SetColor(c color.Color) {
if c == nil {
c = color.Black
}
if e.cur().color != c {
e.cur().color = c
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,
@ -130,17 +130,17 @@ func (e *Canvas) Scale(x, y float64) {
}
func (e *Canvas) Push() {
e.stk = append(e.stk, *e.cur())
e.stack = append(e.stack, *e.context())
e.buf.WriteString("gsave\n")
}
func (e *Canvas) Pop() {
e.stk = e.stk[:len(e.stk)-1]
e.stack = e.stack[:len(e.stack)-1]
e.buf.WriteString("grestore\n")
}
func (e *Canvas) Stroke(path vg.Path) {
if e.cur().width <= 0 {
if e.context().width <= 0 {
return
}
e.trace(path)
@ -178,9 +178,9 @@ func (e *Canvas) trace(path vg.Path) {
}
func (e *Canvas) FillString(fnt vg.Font, pt vg.Point, str string) {
if e.cur().font != fnt.Name() || e.cur().fsize != fnt.Size {
e.cur().font = fnt.Name()
e.cur().fsize = fnt.Size
if e.context().font != fnt.Name() || e.context().fsize != fnt.Size {
e.context().font = fnt.Name()
e.context().fsize = fnt.Size
fmt.Fprintf(e.buf, "/%s findfont %.*g scalefont setfont\n",
fnt.Name(), pr, fnt.Size)
}

View File

@ -31,11 +31,11 @@ const DPI = 90
const pr = 5
type Canvas struct {
svg *svgo.SVG
w, h vg.Length
buf *bytes.Buffer
ht float64
stk []context
svg *svgo.SVG
w, h vg.Length
buf *bytes.Buffer
ht float64
stack []context
}
type context struct {
@ -49,12 +49,12 @@ type context struct {
func New(w, h vg.Length) *Canvas {
buf := new(bytes.Buffer)
c := &Canvas{
svg: svgo.New(buf),
w: w,
h: h,
buf: buf,
ht: w.Points(),
stk: []context{context{}},
svg: svgo.New(buf),
w: w,
h: h,
buf: buf,
ht: w.Points(),
stack: []context{context{}},
}
// This is like svg.Start, except it uses floats
@ -81,69 +81,69 @@ func (c *Canvas) Size() (w, h vg.Length) {
return c.w, c.h
}
func (c *Canvas) cur() *context {
return &c.stk[len(c.stk)-1]
func (c *Canvas) context() *context {
return &c.stack[len(c.stack)-1]
}
func (c *Canvas) SetLineWidth(w vg.Length) {
c.cur().lineWidth = w
c.context().lineWidth = w
}
func (c *Canvas) SetLineDash(dashes []vg.Length, offs vg.Length) {
c.cur().dashArray = dashes
c.cur().dashOffset = offs
c.context().dashArray = dashes
c.context().dashOffset = offs
}
func (c *Canvas) SetColor(clr color.Color) {
c.cur().color = clr
c.context().color = clr
}
func (c *Canvas) Rotate(rot float64) {
rot = rot * 180 / math.Pi
c.svg.Rotate(rot)
c.cur().gEnds++
c.context().gEnds++
}
func (c *Canvas) Translate(pt vg.Point) {
c.svg.Gtransform(fmt.Sprintf("translate(%.*g, %.*g)", pr, pt.X.Dots(DPI), pr, pt.Y.Dots(DPI)))
c.cur().gEnds++
c.context().gEnds++
}
func (c *Canvas) Scale(x, y float64) {
c.svg.ScaleXY(x, y)
c.cur().gEnds++
c.context().gEnds++
}
func (c *Canvas) Push() {
top := *c.cur()
top := *c.context()
top.gEnds = 0
c.stk = append(c.stk, top)
c.stack = append(c.stack, top)
}
func (c *Canvas) Pop() {
for i := 0; i < c.cur().gEnds; i++ {
for i := 0; i < c.context().gEnds; i++ {
c.svg.Gend()
}
c.stk = c.stk[:len(c.stk)-1]
c.stack = c.stack[:len(c.stack)-1]
}
func (c *Canvas) Stroke(path vg.Path) {
if c.cur().lineWidth.Dots(DPI) <= 0 {
if c.context().lineWidth.Dots(DPI) <= 0 {
return
}
c.svg.Path(c.pathData(path),
style(elm("fill", "#000000", "none"),
elm("stroke", "none", colorString(c.cur().color)),
elm("stroke-opacity", "1", opacityString(c.cur().color)),
elm("stroke-width", "1", "%.*g", pr, c.cur().lineWidth.Dots(DPI)),
elm("stroke", "none", colorString(c.context().color)),
elm("stroke-opacity", "1", opacityString(c.context().color)),
elm("stroke-width", "1", "%.*g", pr, c.context().lineWidth.Dots(DPI)),
elm("stroke-dasharray", "none", dashArrayString(c)),
elm("stroke-dashoffset", "0", "%.*g", pr, c.cur().dashOffset.Dots(DPI))))
elm("stroke-dashoffset", "0", "%.*g", pr, c.context().dashOffset.Dots(DPI))))
}
func (c *Canvas) Fill(path vg.Path) {
c.svg.Path(c.pathData(path),
style(elm("fill", "#000000", colorString(c.cur().color)),
elm("fill-opacity", "1", opacityString(c.cur().color))))
style(elm("fill", "#000000", colorString(c.context().color)),
elm("fill-opacity", "1", opacityString(c.context().color))))
}
func (c *Canvas) pathData(path vg.Path) string {
@ -253,7 +253,7 @@ func (c *Canvas) FillString(font vg.Font, pt vg.Point, str string) {
}
sty := style(fontStr,
elm("font-size", "medium", "%.*gpt", pr, font.Size.Points()),
elm("fill", "#000000", colorString(c.cur().color)))
elm("fill", "#000000", colorString(c.context().color)))
if sty != "" {
sty = "\n\t" + sty
}
@ -341,7 +341,7 @@ func (c *Canvas) WriteTo(w io.Writer) (int64, error) {
// needed before the SVG is saved.
func (c *Canvas) nEnds() int {
n := 1 // close the transform that moves the origin
for _, ctx := range c.stk {
for _, ctx := range c.stack {
n += ctx.gEnds
}
return n
@ -383,9 +383,9 @@ func elm(key, def, f string, vls ...interface{}) string {
// dash array specification.
func dashArrayString(c *Canvas) string {
str := ""
for i, d := range c.cur().dashArray {
for i, d := range c.context().dashArray {
str += fmt.Sprintf("%.*g", pr, d.Dots(DPI))
if i < len(c.cur().dashArray)-1 {
if i < len(c.context().dashArray)-1 {
str += ","
}
}