mirror of https://github.com/etcd-io/dbtester.git
vendor: sync with latest
Signed-off-by: Gyuho Lee <gyuhox@gmail.com>
This commit is contained in:
parent
931e864ffd
commit
75ce1a357e
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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]]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 += ","
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue