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

View File

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

View File

@ -134,7 +134,7 @@ sudo rm -rf ${HOME}/*
# GO_VERSION=1.8.3 # GO_VERSION=1.8.3
# etcd v3.3.0 # 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 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 plotly
etcd v3.2.0 (Go 1.8.3) 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) 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 Graph BoxPlot
Traces Traces

View File

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

View File

@ -76,6 +76,14 @@ func Commaf(v float64) string {
return buf.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 // BigComma produces a string form of the given big.Int in base 10
// with commas after every three orders of magnitude. // with commas after every three orders of magnitude.
func BigComma(b *big.Int) string { func BigComma(b *big.Int) string {

View File

@ -1,6 +1,9 @@
package humanize package humanize
import "strconv" import (
"strconv"
"strings"
)
func stripTrailingZeros(s string) string { func stripTrailingZeros(s string) string {
offset := len(s) - 1 offset := len(s) - 1
@ -17,7 +20,27 @@ func stripTrailingZeros(s string) string {
return s[:offset+1] 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. // Ftoa converts a float to a string with no trailing zeros.
func Ftoa(num float64) string { func Ftoa(num float64) string {
return stripTrailingZeros(strconv.FormatFloat(num, 'f', 6, 64)) 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 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") var errInvalid = errors.New("invalid input")
// ParseSI parses an SI string back into the number and unit. // ParseSI parses an SI string back into the number and unit.

View File

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

View File

@ -30,12 +30,33 @@ func ConditionString(cond bool, valid, inValid string) string {
return inValid return inValid
} }
func isNumOrSpace(r rune) bool {
return ('0' <= r && r <= '9') || r == ' '
}
// Format Table Header // Format Table Header
// Replace _ , . and spaces // Replace _ , . and spaces
func Title(name string) string { func Title(name string) string {
name = strings.Replace(name, "_", " ", -1) origLen := len(name)
name = strings.Replace(name, ".", " ", -1) 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) 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) 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. // getLines decomposes a multiline string into a slice of strings.
func getLines(s string) []string { func getLines(s string) []string {
var lines []string return strings.Split(s, nl)
for _, line := range strings.Split(s, nl) {
lines = append(lines, line)
}
return lines
} }

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 { for _, s := range xSpacing {
xTotalSpace += s.n + s.p xTotalSpace += s.n + s.p
} }
xTotalSpace += float64(t.PadX) * float64(len(xSpacing)-1)
var yTotalSpace float64 var yTotalSpace float64
for _, s := range ySpacing { for _, s := range ySpacing {
yTotalSpace += s.n + s.p 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)) 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)) 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 package plot
import ( import (
"math"
"gonum.org/v1/plot/vg" "gonum.org/v1/plot/vg"
"gonum.org/v1/plot/vg/draw" "gonum.org/v1/plot/vg/draw"
) )
@ -67,9 +69,9 @@ type Thumbnailer interface {
Thumbnail(c *draw.Canvas) Thumbnail(c *draw.Canvas)
} }
// makeLegend returns a legend with the default // NewLegend returns a legend with the default
// parameter settings. // parameter settings.
func makeLegend() (Legend, error) { func NewLegend() (Legend, error) {
font, err := vg.MakeFont(DefaultFont, vg.Points(12)) font, err := vg.MakeFont(DefaultFont, vg.Points(12))
if err != nil { if err != nil {
return Legend{}, err return Legend{}, err
@ -80,8 +82,8 @@ func makeLegend() (Legend, error) {
}, nil }, nil
} }
// draw draws the legend to the given draw.Canvas. // Draw draws the legend to the given draw.Canvas.
func (l *Legend) draw(c draw.Canvas) { func (l *Legend) Draw(c draw.Canvas) {
iconx := c.Min.X iconx := c.Min.X
sty := l.TextStyle sty := l.TextStyle
textx := iconx + l.ThumbnailWidth + sty.Rectangle(" ").Max.X 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 // entryHeight returns the height of the tallest legend
// entry text. // entry text.
func (l *Legend) entryHeight() (height vg.Length) { 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 { if err != nil {
return nil, err return nil, err
} }
legend, err := makeLegend() legend, err := NewLegend()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -165,7 +165,7 @@ func (p *Plot) Draw(c draw.Canvas) {
data.Plot(dataC, p) 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 // DataCanvas returns a new draw.Canvas that

View File

@ -9,7 +9,6 @@ import (
"gonum.org/v1/plot" "gonum.org/v1/plot"
"gonum.org/v1/plot/palette" "gonum.org/v1/plot/palette"
"gonum.org/v1/plot/vg"
"gonum.org/v1/plot/vg/draw" "gonum.org/v1/plot/vg/draw"
) )
@ -56,16 +55,10 @@ func (l *ColorBar) check() {
func (l *ColorBar) Plot(c draw.Canvas, p *plot.Plot) { func (l *ColorBar) Plot(c draw.Canvas, p *plot.Plot) {
l.check() l.check()
colors := l.colors(c) colors := l.colors(c)
var img *image.NRGBA64 var pImg *Image
var xmin, xmax, ymin, ymax vg.Length
delta := (l.ColorMap.Max() - l.ColorMap.Min()) / float64(colors) delta := (l.ColorMap.Max() - l.ColorMap.Min()) / float64(colors)
if l.Vertical { if l.Vertical {
trX, trY := p.Transforms(&c) img := image.NewNRGBA64(image.Rectangle{
xmin = trX(0)
ymin = trY(l.ColorMap.Min())
xmax = trX(1)
ymax = trY(l.ColorMap.Max())
img = image.NewNRGBA64(image.Rectangle{
Min: image.Point{X: 0, Y: 0}, Min: image.Point{X: 0, Y: 0},
Max: image.Point{X: 1, Y: colors}, 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) img.Set(0, colors-1-i, color)
} }
pImg = NewImage(img, 0, l.ColorMap.Min(), 1, l.ColorMap.Max())
} else { } else {
trX, trY := p.Transforms(&c) img := image.NewNRGBA64(image.Rectangle{
xmin = trX(l.ColorMap.Min())
ymin = trY(0)
xmax = trX(l.ColorMap.Max())
ymax = trY(1)
img = image.NewNRGBA64(image.Rectangle{
Min: image.Point{X: 0, Y: 0}, Min: image.Point{X: 0, Y: 0},
Max: image.Point{X: colors, Y: 1}, 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) img.Set(i, 0, color)
} }
pImg = NewImage(img, l.ColorMap.Min(), 0, l.ColorMap.Max(), 1)
} }
rect := vg.Rectangle{ pImg.Plot(c, p)
Min: vg.Point{X: xmin, Y: ymin},
Max: vg.Point{X: xmax, Y: ymax},
}
c.DrawImage(rect, img)
} }
// DataRange implements the DataRange method // 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}, Min: vg.Point{X: xmin, Y: ymin},
Max: vg.Point{X: xmax, Y: ymax}, Max: vg.Point{X: xmax, Y: ymax},
} }
c.DrawImage(rect, img.img) c.DrawImage(rect, img.transformFor(p))
} }
// DataRange implements the DataRange method // DataRange implements the DataRange method
@ -71,6 +71,48 @@ func (img *Image) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox {
return nil 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 { func (img *Image) x(c int) float64 {
if c >= img.cols || c < 0 { if c >= img.cols || c < 0 {
panic("plotter/image: illegal range") panic("plotter/image: illegal range")

View File

@ -23,12 +23,12 @@ import (
const DPI = 72 const DPI = 72
type Canvas struct { type Canvas struct {
stk []ctx stack []context
w, h vg.Length w, h vg.Length
buf *bytes.Buffer buf *bytes.Buffer
} }
type ctx struct { type context struct {
color color.Color color color.Color
width vg.Length width vg.Length
dashes []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. // NewTitle returns a new Canvas with the given title string.
func NewTitle(w, h vg.Length, title string) *Canvas { func NewTitle(w, h vg.Length, title string) *Canvas {
c := &Canvas{ c := &Canvas{
stk: []ctx{ctx{}}, stack: []context{context{}},
w: w, w: w,
h: h, h: h,
buf: new(bytes.Buffer), buf: new(bytes.Buffer),
} }
c.buf.WriteString("%%!PS-Adobe-3.0 EPSF-3.0\n") 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("%%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 return c.w, c.h
} }
// cur returns the top context on the stack. // context returns the top context on the stack.
func (e *Canvas) cur() *ctx { func (e *Canvas) context() *context {
return &e.stk[len(e.stk)-1] return &e.stack[len(e.stack)-1]
} }
func (e *Canvas) SetLineWidth(w vg.Length) { func (e *Canvas) SetLineWidth(w vg.Length) {
if e.cur().width != w { if e.context().width != w {
e.cur().width = w e.context().width = w
fmt.Fprintf(e.buf, "%.*g setlinewidth\n", pr, w.Dots(DPI)) fmt.Fprintf(e.buf, "%.*g setlinewidth\n", pr, w.Dots(DPI))
} }
} }
func (e *Canvas) SetLineDash(dashes []vg.Length, o vg.Length) { func (e *Canvas) SetLineDash(dashes []vg.Length, o vg.Length) {
cur := e.cur().dashes cur := e.context().dashes
dashEq := len(dashes) == len(cur) dashEq := len(dashes) == len(cur)
for i := 0; dashEq && i < len(dashes); i++ { for i := 0; dashEq && i < len(dashes); i++ {
if dashes[i] != cur[i] { if dashes[i] != cur[i] {
dashEq = false dashEq = false
} }
} }
if !dashEq || e.cur().offs != o { if !dashEq || e.context().offs != o {
e.cur().dashes = dashes e.context().dashes = dashes
e.cur().offs = o e.context().offs = o
e.buf.WriteString("[") e.buf.WriteString("[")
for _, d := range dashes { for _, d := range dashes {
fmt.Fprintf(e.buf, " %.*g", pr, d.Dots(DPI)) fmt.Fprintf(e.buf, " %.*g", pr, d.Dots(DPI))
@ -107,8 +107,8 @@ func (e *Canvas) SetColor(c color.Color) {
if c == nil { if c == nil {
c = color.Black c = color.Black
} }
if e.cur().color != c { if e.context().color != c {
e.cur().color = c e.context().color = c
r, g, b, _ := c.RGBA() r, g, b, _ := c.RGBA()
mx := float64(math.MaxUint16) mx := float64(math.MaxUint16)
fmt.Fprintf(e.buf, "%.*g %.*g %.*g setrgbcolor\n", pr, float64(r)/mx, 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() { func (e *Canvas) Push() {
e.stk = append(e.stk, *e.cur()) e.stack = append(e.stack, *e.context())
e.buf.WriteString("gsave\n") e.buf.WriteString("gsave\n")
} }
func (e *Canvas) Pop() { 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") e.buf.WriteString("grestore\n")
} }
func (e *Canvas) Stroke(path vg.Path) { func (e *Canvas) Stroke(path vg.Path) {
if e.cur().width <= 0 { if e.context().width <= 0 {
return return
} }
e.trace(path) 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) { func (e *Canvas) FillString(fnt vg.Font, pt vg.Point, str string) {
if e.cur().font != fnt.Name() || e.cur().fsize != fnt.Size { if e.context().font != fnt.Name() || e.context().fsize != fnt.Size {
e.cur().font = fnt.Name() e.context().font = fnt.Name()
e.cur().fsize = fnt.Size e.context().fsize = fnt.Size
fmt.Fprintf(e.buf, "/%s findfont %.*g scalefont setfont\n", fmt.Fprintf(e.buf, "/%s findfont %.*g scalefont setfont\n",
fnt.Name(), pr, fnt.Size) fnt.Name(), pr, fnt.Size)
} }

View File

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