diff --git a/go.mod b/go.mod
index d95f1b4..32eabd1 100644
--- a/go.mod
+++ b/go.mod
@@ -1,14 +1,25 @@
module github.com/rs/jplot
-go 1.14
+go 1.17
require (
github.com/dustin/go-humanize v1.0.0
github.com/elgs/gojq v0.0.0-20160421194050-81fa9a608a13
+ github.com/mattn/go-isatty v0.0.14
+ github.com/mattn/go-sixel v0.0.1
+ github.com/monochromegane/terminal v0.0.0-20161222050454-9bc47e2707d9
+ github.com/wcharczuk/go-chart/v2 v2.1.0
+ golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c
+)
+
+require (
+ github.com/blend/go-sdk v1.20211204.3 // indirect
github.com/elgs/gosplitargs v0.0.0-20161028071935-a491c5eeb3c8 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
- github.com/monochromegane/terminal v0.0.0-20161222050454-9bc47e2707d9
- github.com/wcharczuk/go-chart v2.0.1+incompatible
- golang.org/x/crypto v0.0.0-20200414173820-0848c9571904
- golang.org/x/image v0.0.0-20200119044424-58c23975cae1 // indirect
+ github.com/soniakeys/quant v1.0.0 // indirect
+ github.com/stretchr/testify v1.7.0 // indirect
+ github.com/wcharczuk/go-chart v2.0.1+incompatible // indirect
+ golang.org/x/image v0.0.0-20200927104501-e162460cd6b5 // indirect
+ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect
+ golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 // indirect
)
diff --git a/go.sum b/go.sum
index f690896..13bbfc3 100644
--- a/go.sum
+++ b/go.sum
@@ -1,21 +1,654 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
+cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
+cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
+cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
+cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
+cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
+cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
+cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
+cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
+cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
+cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
+cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
+cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
+cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
+cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
+cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
+cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
+cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
+cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
+cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
+cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
+cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
+cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
+cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
+cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
+cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
+cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
+cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
+cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
+cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
+cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
+dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
+github.com/DataDog/datadog-go v4.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
+github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
+github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
+github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
+github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
+github.com/aws/aws-sdk-go v1.38.41/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
+github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
+github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
+github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
+github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
+github.com/blend/go-sdk v1.20211204.3 h1:Bgj+Z79Q0toRE/hR2Tsdck+XSiwajTg8bGRF2enQ4yI=
+github.com/blend/go-sdk v1.20211204.3/go.mod h1:nbmX7cdPm66JOqg6M3cKMtuqj6RzkE72sHZue61T5c0=
+github.com/blend/sentry-go v1.0.1/go.mod h1:hgyX3WXen2YBiA0NitlfsXsvS+9ly2YlEBmmmYDgrWY=
+github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
+github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
+github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
+github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
+github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
+github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
+github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
+github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
+github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
+github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/elgs/gojq v0.0.0-20160421194050-81fa9a608a13 h1:/voSflvo4UvPT0XZy+YQMuUk04topJ2swxyOOyXefMM=
github.com/elgs/gojq v0.0.0-20160421194050-81fa9a608a13/go.mod h1:rQELVIqRXpraeUryHOBadz99ePvEVQmTVpGr8M9QQ4Q=
github.com/elgs/gosplitargs v0.0.0-20161028071935-a491c5eeb3c8 h1:bD2/rCXwgXJm2vgoSSSCM9IPjVFfEoQFFblzg7HHABI=
github.com/elgs/gosplitargs v0.0.0-20161028071935-a491c5eeb3c8/go.mod h1:o4DgpccPNAQAlPSxo7I4L/LWNh2oyr/BBGSynrLTmZM=
+github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
+github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
+github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/go-errors/errors v1.1.1/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
+github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
+github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
+github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
+github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
+github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
+github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
+github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
+github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
+github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
+github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
+github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
+github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
+github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
+github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
+github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
+github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
+github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
+github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
+github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
+github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
+github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
+github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
+github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
+github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
+github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
+github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
+github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
+github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
+github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
+github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
+github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
+github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
+github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
+github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
+github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
+github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
+github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
+github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=
+github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=
+github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=
+github.com/jackc/pgconn v1.4.0/go.mod h1:Y2O3ZDF0q4mMacyWV3AstPJpeHXWGEetiFttmq5lahk=
+github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
+github.com/jackc/pgconn v1.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
+github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
+github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
+github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
+github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
+github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
+github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
+github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
+github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
+github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
+github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
+github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
+github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
+github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
+github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
+github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
+github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
+github.com/jackc/pgtype v1.2.0/go.mod h1:5m2OfMh1wTK7x+Fk952IDmI4nw3nPrvtQdM0ZT4WpC0=
+github.com/jackc/pgtype v1.3.1-0.20200510190516-8cd94a14c75a/go.mod h1:vaogEUkALtxZMCH411K+tKzNpwzCKU+AnPzBKZ+I+Po=
+github.com/jackc/pgtype v1.3.1-0.20200606141011-f6355165a91c/go.mod h1:cvk9Bgu/VzJ9/lxTO5R5sf80p0DiucVtN7ZxvaC4GmQ=
+github.com/jackc/pgtype v1.6.2/go.mod h1:JCULISAZBFGrHaOXIIFiyfzW5VY0GRitRr8NeJsrdig=
+github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
+github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
+github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
+github.com/jackc/pgx/v4 v4.5.0/go.mod h1:EpAKPLdnTorwmPUUsqrPxy5fphV18j9q3wrfRXgo+kA=
+github.com/jackc/pgx/v4 v4.6.1-0.20200510190926-94ba730bb1e9/go.mod h1:t3/cdRQl6fOLDxqtlyhe9UWgfIi9R8+8v8GKV5TRA/o=
+github.com/jackc/pgx/v4 v4.6.1-0.20200606145419-4e5062306904/go.mod h1:ZDaNWkt9sW1JMiNn0kdYBaLelIhw7Pg4qd+Vk6tw7Hg=
+github.com/jackc/pgx/v4 v4.10.0/go.mod h1:QlrWebbs3kqEZPHCTGyxecvzG6tvIsYu+A5b1raylkA=
+github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
+github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
+github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
+github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
+github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
+github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
+github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
+github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
+github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
+github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
+github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
+github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
+github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
+github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
+github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
+github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
+github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
+github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
+github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
+github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
+github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
+github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
+github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
+github.com/mattn/go-sixel v0.0.1 h1:rhJSpux2xjsmXdXqY694uiEC0Rwxt6jYoq7Bahqo2xs=
+github.com/mattn/go-sixel v0.0.1/go.mod h1:zlzhYSuMbLdRdrxfutExxGpC+Pf2uUTJ6GpVQ4LB5dc=
+github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/mediocregopher/radix/v4 v4.0.0-beta.1/go.mod h1:Z74pilm773ghbGV4EEoPvi6XWgkAfr0VCNkfa8gI1PU=
+github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
+github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
+github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
+github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
+github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
+github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/monochromegane/terminal v0.0.0-20161222050454-9bc47e2707d9 h1:YhsMshmD0JlqM4ss3JBY6Ul4QLigGvBtShFYHkb3JGg=
github.com/monochromegane/terminal v0.0.0-20161222050454-9bc47e2707d9/go.mod h1:9N3QHEQ4Ov/dAnHmOIJ8ffm8O1iQfCPfso+PpakXPsY=
+github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
+github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
+github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
+github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
+github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
+github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
+github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
+github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
+github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
+github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
+github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
+github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
+github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
+github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
+github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
+github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
+github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
+github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
+github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
+github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
+github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
+github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
+github.com/soniakeys/quant v1.0.0 h1:N1um9ktjbkZVcywBVAAYpZYSHxEfJGzshHCxx/DaI0Y=
+github.com/soniakeys/quant v1.0.0/go.mod h1:HI1k023QuVbD4H8i9YdfZP2munIHU4QpjsImz6Y6zds=
+github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
+github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
+github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
+github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
+github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
+github.com/tilinna/clock v1.0.2/go.mod h1:ZsP7BcY7sEEz7ktc0IVy8Us6boDrK8VradlKRUGfOao=
+github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/wcharczuk/go-chart v2.0.1+incompatible h1:0pz39ZAycJFF7ju/1mepnk26RLVLBCWz1STcD3doU0A=
github.com/wcharczuk/go-chart v2.0.1+incompatible/go.mod h1:PF5tmL4EIx/7Wf+hEkpCqYi5He4u90sw+0+6FhrryuE=
+github.com/wcharczuk/go-chart/v2 v2.1.0 h1:tY2slqVQ6bN+yHSnDYwZebLQFkphK4WNrVwnt7CJZ2I=
+github.com/wcharczuk/go-chart/v2 v2.1.0/go.mod h1:yx7MvAVNcP/kN9lKXM/NTce4au4DFN99j6i1OwDclNA=
+github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
+github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
+go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
+go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
+go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
+go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
+go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
+go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
+go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
+go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
+go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
+golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20200414173820-0848c9571904 h1:bXoxMPcSLOq08zI3/c5dEBT6lE4eh+jOh886GHrn6V8=
-golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/image v0.0.0-20200119044424-58c23975cae1 h1:5h3ngYt7+vXCDZCup/HkCQgW5XwmSvR/nA2JmJ0RErg=
-golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
+golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
+golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c h1:9HhBz5L/UjnK9XLtiZhYAdue5BVKep3PMmS2LuPDt8k=
+golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
+golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
+golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
+golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
+golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
+golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
+golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
+golang.org/x/image v0.0.0-20200927104501-e162460cd6b5 h1:QelT11PB4FXiDEXucrfNckHoFxwt8USGY1ajP1ZF5lM=
+golang.org/x/image v0.0.0-20200927104501-e162460cd6b5/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
+golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
+golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
+golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
+golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
+golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20201203001011-0b49973bad19/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
+golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
+golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM=
+golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
+golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
+golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
+golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
+golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
+golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
+golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
+golang.org/x/tools v0.0.0-20201204162204-73cf035baebf/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
+google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
+google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
+google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
+google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
+google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
+google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
+google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
+google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
+google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
+google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
+google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
+google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
+google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
+google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
+google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
+google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
+google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
+google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
+google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
+google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
+google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
+google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
+google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
+gopkg.in/DataDog/dd-trace-go.v1 v1.27.1/go.mod h1:Sp1lku8WJMvNV0kjDI4Ni/T7J/U3BO5ct5kEaoVU8+I=
+gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
+gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
+gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
+gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
+honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
+honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
+rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
+rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
+rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
diff --git a/graph/dash.go b/graph/dash.go
index e589953..b69a4e8 100644
--- a/graph/dash.go
+++ b/graph/dash.go
@@ -7,7 +7,7 @@ import (
"io"
"github.com/rs/jplot/data"
- chart "github.com/wcharczuk/go-chart"
+ chart "github.com/wcharczuk/go-chart/v2"
)
type Dash struct {
diff --git a/graph/graph.go b/graph/graph.go
index ed56a42..85c8e7d 100644
--- a/graph/graph.go
+++ b/graph/graph.go
@@ -6,8 +6,8 @@ import (
humanize "github.com/dustin/go-humanize"
"github.com/rs/jplot/data"
- chart "github.com/wcharczuk/go-chart"
- "github.com/wcharczuk/go-chart/drawing"
+ chart "github.com/wcharczuk/go-chart/v2"
+ "github.com/wcharczuk/go-chart/v2/drawing"
)
func init() {
diff --git a/graph/legend.go b/graph/legend.go
index 7cc7bfe..fc52b23 100644
--- a/graph/legend.go
+++ b/graph/legend.go
@@ -3,8 +3,8 @@ package graph
import (
"math"
- chart "github.com/wcharczuk/go-chart"
- "github.com/wcharczuk/go-chart/drawing"
+ chart "github.com/wcharczuk/go-chart/v2"
+ "github.com/wcharczuk/go-chart/v2/drawing"
)
// custom version of chart.Legend
diff --git a/main.go b/main.go
index e7f35e5..5bcd276 100644
--- a/main.go
+++ b/main.go
@@ -3,6 +3,7 @@ package main // import "github.com/rs/jplot"
import (
"flag"
"fmt"
+ "github.com/rs/jplot/streamer"
"os"
"os/signal"
"strings"
@@ -28,6 +29,10 @@ func main() {
fmt.Fprintln(out, " option:")
fmt.Fprintln(out, " - counter: Computes the difference with the last value. The value must increase monotonically.")
fmt.Fprintln(out, " - marker: When the value is none-zero, a vertical line is drawn.")
+ fmt.Fprintln(out, " - port: launches a web-server that will be drawing images. This disables drawing in terminal")
+ fmt.Fprintln(out, " - hiw (http image width): defines web server's image width")
+ fmt.Fprintln(out, " - hih (http image height): defines web server's image width")
+ fmt.Fprintln(out, " - hirr (http image refresh rate): how often image will be refreshed (milliseconds)")
fmt.Fprintln(out, " path:")
fmt.Fprintln(out, " JSON field path (eg: field.sub-field).")
}
@@ -36,15 +41,12 @@ func main() {
" Note that counter fields are computed based on this interval.")
steps := flag.Int("steps", 100, "Number of values to plot.")
rows := flag.Int("rows", 0, "Limits the height of the graph output.")
+ port := flag.Int("port", 0, "web server port")
+ hiw := flag.Int("hiw", 500, "web server image height")
+ hih := flag.Int("hih", 500, "web server image width")
+ hirr := flag.Int("hirr", 1000, "web server refresh rate (milliseconds)")
flag.Parse()
- if !term.HasGraphicsSupport() {
- fatal("iTerm2 or DRCS Sixel graphics required")
- }
- if os.Getenv("TERM") == "screen" {
- fatal("screen and tmux not supported")
- }
-
if len(flag.Args()) == 0 {
flag.Usage()
os.Exit(1)
@@ -66,6 +68,25 @@ func main() {
Specs: specs,
Data: dp,
}
+ go func() {
+ if er := dp.Run(specs); er != nil {
+ fatal("Data source error: ", er)
+ }
+ }()
+
+ if *port != 0 {
+ err := streamer.Start(*port, &dash, *hiw, *hih, *hirr)
+ if err != nil {
+ fatal("cannot start server: %s", err.Error())
+ }
+ }
+
+ if !term.HasGraphicsSupport() {
+ fatal("iTerm2 or DRCS Sixel graphics required")
+ }
+ if os.Getenv("TERM") == "screen" {
+ fatal("screen and tmux not supported")
+ }
wg := &sync.WaitGroup{}
wg.Add(1)
@@ -106,9 +127,6 @@ func main() {
}
}()
- if err := dp.Run(specs); err != nil {
- fatal("Data source error: ", err)
- }
}
func fatal(a ...interface{}) {
@@ -149,9 +167,9 @@ func render(dash graph.Dash, rows int) {
rows = size.Row
}
// Use iTerm2 image display feature.
- term := term.NewImageWriter(width, height)
- defer term.Close()
- if err := dash.Render(term, width, height); err != nil {
+ tm := term.NewImageWriter(width, height)
+ defer tm.Close()
+ if err := dash.Render(tm, width, height); err != nil {
fatal(fmt.Sprintf("cannot render graph: %v", err.Error()))
}
}
diff --git a/streamer/start.go b/streamer/start.go
new file mode 100644
index 0000000..00e896b
--- /dev/null
+++ b/streamer/start.go
@@ -0,0 +1,31 @@
+package streamer
+
+import (
+ "fmt"
+ "github.com/rs/jplot/graph"
+ "log"
+ "net"
+ "net/http"
+)
+
+func Start(port int, dash *graph.Dash, width, height, refreshRate int) error {
+ listener, err := net.Listen("tcp", fmt.Sprintf("0.0.0.0:%d", port))
+ if err != nil {
+ return err
+ }
+
+ stream := &Streamer{
+ dash: dash,
+ width: width,
+ height: height,
+ refreshRate: refreshRate,
+ }
+
+ log.Println("starting server on ", port)
+ er := http.Serve(listener, stream)
+ if er != nil {
+ return er
+ }
+
+ return nil
+}
diff --git a/streamer/streamer.go b/streamer/streamer.go
new file mode 100644
index 0000000..244ffcc
--- /dev/null
+++ b/streamer/streamer.go
@@ -0,0 +1,62 @@
+package streamer
+
+import (
+ "errors"
+ "fmt"
+ "github.com/rs/jplot/graph"
+ "net/http"
+ "syscall"
+)
+
+type Streamer struct {
+ dash *graph.Dash
+
+ width, height, refreshRate int
+}
+
+func (s *Streamer) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
+ if req.URL.String() == "/" {
+ err := s.serveStatic(resp)
+ if err != nil {
+ resp.WriteHeader(http.StatusInternalServerError)
+ fmt.Println("error on serving static: ", err)
+ }
+ return
+ }
+
+ resp.Header().Add("Content-Type", "image/png")
+ err := s.dash.Render(resp, s.width, s.height)
+ if err == nil {
+ return
+ }
+
+ if errors.Is(err, syscall.EPIPE) {
+ return
+ }
+
+ fmt.Println("error on render: ", err)
+}
+
+func (s *Streamer) serveStatic(resp http.ResponseWriter) error {
+ resp.Header().Add("Content-Type", "text/html")
+ _, err := resp.Write([]byte(fmt.Sprintf(`
+
+
+
+
+
+
+
+
+
+`, s.refreshRate)))
+
+ return err
+}
diff --git a/vendor/github.com/mattn/go-isatty/LICENSE b/vendor/github.com/mattn/go-isatty/LICENSE
new file mode 100644
index 0000000..65dc692
--- /dev/null
+++ b/vendor/github.com/mattn/go-isatty/LICENSE
@@ -0,0 +1,9 @@
+Copyright (c) Yasuhiro MATSUMOTO
+
+MIT License (Expat)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/github.com/mattn/go-isatty/README.md b/vendor/github.com/mattn/go-isatty/README.md
new file mode 100644
index 0000000..3841835
--- /dev/null
+++ b/vendor/github.com/mattn/go-isatty/README.md
@@ -0,0 +1,50 @@
+# go-isatty
+
+[](http://godoc.org/github.com/mattn/go-isatty)
+[](https://codecov.io/gh/mattn/go-isatty)
+[](https://coveralls.io/github/mattn/go-isatty?branch=master)
+[](https://goreportcard.com/report/mattn/go-isatty)
+
+isatty for golang
+
+## Usage
+
+```go
+package main
+
+import (
+ "fmt"
+ "github.com/mattn/go-isatty"
+ "os"
+)
+
+func main() {
+ if isatty.IsTerminal(os.Stdout.Fd()) {
+ fmt.Println("Is Terminal")
+ } else if isatty.IsCygwinTerminal(os.Stdout.Fd()) {
+ fmt.Println("Is Cygwin/MSYS2 Terminal")
+ } else {
+ fmt.Println("Is Not Terminal")
+ }
+}
+```
+
+## Installation
+
+```
+$ go get github.com/mattn/go-isatty
+```
+
+## License
+
+MIT
+
+## Author
+
+Yasuhiro Matsumoto (a.k.a mattn)
+
+## Thanks
+
+* k-takata: base idea for IsCygwinTerminal
+
+ https://github.com/k-takata/go-iscygpty
diff --git a/vendor/github.com/mattn/go-isatty/doc.go b/vendor/github.com/mattn/go-isatty/doc.go
new file mode 100644
index 0000000..17d4f90
--- /dev/null
+++ b/vendor/github.com/mattn/go-isatty/doc.go
@@ -0,0 +1,2 @@
+// Package isatty implements interface to isatty
+package isatty
diff --git a/vendor/github.com/mattn/go-isatty/go.test.sh b/vendor/github.com/mattn/go-isatty/go.test.sh
new file mode 100644
index 0000000..012162b
--- /dev/null
+++ b/vendor/github.com/mattn/go-isatty/go.test.sh
@@ -0,0 +1,12 @@
+#!/usr/bin/env bash
+
+set -e
+echo "" > coverage.txt
+
+for d in $(go list ./... | grep -v vendor); do
+ go test -race -coverprofile=profile.out -covermode=atomic "$d"
+ if [ -f profile.out ]; then
+ cat profile.out >> coverage.txt
+ rm profile.out
+ fi
+done
diff --git a/vendor/github.com/mattn/go-isatty/isatty_bsd.go b/vendor/github.com/mattn/go-isatty/isatty_bsd.go
new file mode 100644
index 0000000..39bbcf0
--- /dev/null
+++ b/vendor/github.com/mattn/go-isatty/isatty_bsd.go
@@ -0,0 +1,19 @@
+//go:build (darwin || freebsd || openbsd || netbsd || dragonfly) && !appengine
+// +build darwin freebsd openbsd netbsd dragonfly
+// +build !appengine
+
+package isatty
+
+import "golang.org/x/sys/unix"
+
+// IsTerminal return true if the file descriptor is terminal.
+func IsTerminal(fd uintptr) bool {
+ _, err := unix.IoctlGetTermios(int(fd), unix.TIOCGETA)
+ return err == nil
+}
+
+// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2
+// terminal. This is also always false on this environment.
+func IsCygwinTerminal(fd uintptr) bool {
+ return false
+}
diff --git a/vendor/github.com/mattn/go-isatty/isatty_others.go b/vendor/github.com/mattn/go-isatty/isatty_others.go
new file mode 100644
index 0000000..3150322
--- /dev/null
+++ b/vendor/github.com/mattn/go-isatty/isatty_others.go
@@ -0,0 +1,16 @@
+//go:build appengine || js || nacl || wasm
+// +build appengine js nacl wasm
+
+package isatty
+
+// IsTerminal returns true if the file descriptor is terminal which
+// is always false on js and appengine classic which is a sandboxed PaaS.
+func IsTerminal(fd uintptr) bool {
+ return false
+}
+
+// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2
+// terminal. This is also always false on this environment.
+func IsCygwinTerminal(fd uintptr) bool {
+ return false
+}
diff --git a/vendor/github.com/mattn/go-isatty/isatty_plan9.go b/vendor/github.com/mattn/go-isatty/isatty_plan9.go
new file mode 100644
index 0000000..bae7f9b
--- /dev/null
+++ b/vendor/github.com/mattn/go-isatty/isatty_plan9.go
@@ -0,0 +1,23 @@
+//go:build plan9
+// +build plan9
+
+package isatty
+
+import (
+ "syscall"
+)
+
+// IsTerminal returns true if the given file descriptor is a terminal.
+func IsTerminal(fd uintptr) bool {
+ path, err := syscall.Fd2path(int(fd))
+ if err != nil {
+ return false
+ }
+ return path == "/dev/cons" || path == "/mnt/term/dev/cons"
+}
+
+// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2
+// terminal. This is also always false on this environment.
+func IsCygwinTerminal(fd uintptr) bool {
+ return false
+}
diff --git a/vendor/github.com/mattn/go-isatty/isatty_solaris.go b/vendor/github.com/mattn/go-isatty/isatty_solaris.go
new file mode 100644
index 0000000..0c3acf2
--- /dev/null
+++ b/vendor/github.com/mattn/go-isatty/isatty_solaris.go
@@ -0,0 +1,21 @@
+//go:build solaris && !appengine
+// +build solaris,!appengine
+
+package isatty
+
+import (
+ "golang.org/x/sys/unix"
+)
+
+// IsTerminal returns true if the given file descriptor is a terminal.
+// see: https://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libc/port/gen/isatty.c
+func IsTerminal(fd uintptr) bool {
+ _, err := unix.IoctlGetTermio(int(fd), unix.TCGETA)
+ return err == nil
+}
+
+// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2
+// terminal. This is also always false on this environment.
+func IsCygwinTerminal(fd uintptr) bool {
+ return false
+}
diff --git a/vendor/github.com/mattn/go-isatty/isatty_tcgets.go b/vendor/github.com/mattn/go-isatty/isatty_tcgets.go
new file mode 100644
index 0000000..6778765
--- /dev/null
+++ b/vendor/github.com/mattn/go-isatty/isatty_tcgets.go
@@ -0,0 +1,19 @@
+//go:build (linux || aix || zos) && !appengine
+// +build linux aix zos
+// +build !appengine
+
+package isatty
+
+import "golang.org/x/sys/unix"
+
+// IsTerminal return true if the file descriptor is terminal.
+func IsTerminal(fd uintptr) bool {
+ _, err := unix.IoctlGetTermios(int(fd), unix.TCGETS)
+ return err == nil
+}
+
+// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2
+// terminal. This is also always false on this environment.
+func IsCygwinTerminal(fd uintptr) bool {
+ return false
+}
diff --git a/vendor/github.com/mattn/go-isatty/isatty_windows.go b/vendor/github.com/mattn/go-isatty/isatty_windows.go
new file mode 100644
index 0000000..8e3c991
--- /dev/null
+++ b/vendor/github.com/mattn/go-isatty/isatty_windows.go
@@ -0,0 +1,125 @@
+//go:build windows && !appengine
+// +build windows,!appengine
+
+package isatty
+
+import (
+ "errors"
+ "strings"
+ "syscall"
+ "unicode/utf16"
+ "unsafe"
+)
+
+const (
+ objectNameInfo uintptr = 1
+ fileNameInfo = 2
+ fileTypePipe = 3
+)
+
+var (
+ kernel32 = syscall.NewLazyDLL("kernel32.dll")
+ ntdll = syscall.NewLazyDLL("ntdll.dll")
+ procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
+ procGetFileInformationByHandleEx = kernel32.NewProc("GetFileInformationByHandleEx")
+ procGetFileType = kernel32.NewProc("GetFileType")
+ procNtQueryObject = ntdll.NewProc("NtQueryObject")
+)
+
+func init() {
+ // Check if GetFileInformationByHandleEx is available.
+ if procGetFileInformationByHandleEx.Find() != nil {
+ procGetFileInformationByHandleEx = nil
+ }
+}
+
+// IsTerminal return true if the file descriptor is terminal.
+func IsTerminal(fd uintptr) bool {
+ var st uint32
+ r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0)
+ return r != 0 && e == 0
+}
+
+// Check pipe name is used for cygwin/msys2 pty.
+// Cygwin/MSYS2 PTY has a name like:
+// \{cygwin,msys}-XXXXXXXXXXXXXXXX-ptyN-{from,to}-master
+func isCygwinPipeName(name string) bool {
+ token := strings.Split(name, "-")
+ if len(token) < 5 {
+ return false
+ }
+
+ if token[0] != `\msys` &&
+ token[0] != `\cygwin` &&
+ token[0] != `\Device\NamedPipe\msys` &&
+ token[0] != `\Device\NamedPipe\cygwin` {
+ return false
+ }
+
+ if token[1] == "" {
+ return false
+ }
+
+ if !strings.HasPrefix(token[2], "pty") {
+ return false
+ }
+
+ if token[3] != `from` && token[3] != `to` {
+ return false
+ }
+
+ if token[4] != "master" {
+ return false
+ }
+
+ return true
+}
+
+// getFileNameByHandle use the undocomented ntdll NtQueryObject to get file full name from file handler
+// since GetFileInformationByHandleEx is not available under windows Vista and still some old fashion
+// guys are using Windows XP, this is a workaround for those guys, it will also work on system from
+// Windows vista to 10
+// see https://stackoverflow.com/a/18792477 for details
+func getFileNameByHandle(fd uintptr) (string, error) {
+ if procNtQueryObject == nil {
+ return "", errors.New("ntdll.dll: NtQueryObject not supported")
+ }
+
+ var buf [4 + syscall.MAX_PATH]uint16
+ var result int
+ r, _, e := syscall.Syscall6(procNtQueryObject.Addr(), 5,
+ fd, objectNameInfo, uintptr(unsafe.Pointer(&buf)), uintptr(2*len(buf)), uintptr(unsafe.Pointer(&result)), 0)
+ if r != 0 {
+ return "", e
+ }
+ return string(utf16.Decode(buf[4 : 4+buf[0]/2])), nil
+}
+
+// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2
+// terminal.
+func IsCygwinTerminal(fd uintptr) bool {
+ if procGetFileInformationByHandleEx == nil {
+ name, err := getFileNameByHandle(fd)
+ if err != nil {
+ return false
+ }
+ return isCygwinPipeName(name)
+ }
+
+ // Cygwin/msys's pty is a pipe.
+ ft, _, e := syscall.Syscall(procGetFileType.Addr(), 1, fd, 0, 0)
+ if ft != fileTypePipe || e != 0 {
+ return false
+ }
+
+ var buf [2 + syscall.MAX_PATH]uint16
+ r, _, e := syscall.Syscall6(procGetFileInformationByHandleEx.Addr(),
+ 4, fd, fileNameInfo, uintptr(unsafe.Pointer(&buf)),
+ uintptr(len(buf)*2), 0, 0)
+ if r == 0 || e != 0 {
+ return false
+ }
+
+ l := *(*uint32)(unsafe.Pointer(&buf))
+ return isCygwinPipeName(string(utf16.Decode(buf[2 : 2+l/2])))
+}
diff --git a/vendor/github.com/mattn/go-sixel/README.md b/vendor/github.com/mattn/go-sixel/README.md
new file mode 100644
index 0000000..a629bcd
--- /dev/null
+++ b/vendor/github.com/mattn/go-sixel/README.md
@@ -0,0 +1,44 @@
+# go-sixel
+
+
+
+## Installation
+
+```
+$ go get github.com/mattn/go-sixel
+```
+
+You can install gosr (go sixel renderer), gosd (go sixel decoder) with following installation instruction.
+
+```
+$ go get github.com/mattn/go-sixel/cmd/gosr
+$ go get github.com/mattn/go-sixel/cmd/gosd
+```
+
+## Usage
+
+Encode
+```
+$ cat foo.png | gosr
+```
+
+Decode
+
+```
+$ cat foo.drcs | gosd > foo.png
+```
+
+Use as library
+
+```go
+img, _, _ := image.Decode(filename)
+sixel.NewEncoder(os.Stdout).Encode(img)
+```
+
+## License
+
+MIT
+
+## Author
+
+Yasuhiro Matsumoto (a.k.a mattn)
diff --git a/vendor/github.com/mattn/go-sixel/sixel.go b/vendor/github.com/mattn/go-sixel/sixel.go
new file mode 100644
index 0000000..1eb874c
--- /dev/null
+++ b/vendor/github.com/mattn/go-sixel/sixel.go
@@ -0,0 +1,386 @@
+package sixel
+
+import (
+ "bufio"
+ "bytes"
+ "errors"
+ "fmt"
+ "image"
+ "image/color"
+ "image/draw"
+ "io"
+ "os"
+ "strings"
+
+ "github.com/soniakeys/quant/median"
+)
+
+// Encoder encode image to sixel format
+type Encoder struct {
+ w io.Writer
+ Dither bool
+ Width int
+ Height int
+}
+
+// NewEncoder return new instance of Encoder
+func NewEncoder(w io.Writer) *Encoder {
+ return &Encoder{w: w}
+}
+
+const (
+ specialChNr = byte(0x6d)
+ specialChCr = byte(0x64)
+)
+
+// Encode do encoding
+func (e *Encoder) Encode(img image.Image) error {
+ nc := 255 // (>= 2, 8bit, index 0 is reserved for transparent key color)
+ width, height := img.Bounds().Dx(), img.Bounds().Dy()
+ if width == 0 || height == 0 {
+ return nil
+ }
+ if e.Width > 0 {
+ width = e.Width
+ }
+ if e.Height > 0 {
+ height = e.Height
+ }
+
+ // make adaptive palette using median cut alogrithm
+ q := median.Quantizer(nc - 1)
+ paletted := q.Paletted(img)
+ if e.Dither {
+ // copy source image to new image with applying floyd-stenberg dithering
+ draw.FloydSteinberg.Draw(paletted, img.Bounds(), img, image.ZP)
+ } else {
+ draw.Draw(paletted, img.Bounds(), img, image.ZP, draw.Over)
+ }
+ // use on-memory output buffer for improving the performance
+ var w io.Writer
+ if _, ok := e.w.(*os.File); ok {
+ w = bytes.NewBuffer(make([]byte, 0, 1024*32))
+ } else {
+ w = e.w
+ }
+ // DECSIXEL Introducer(\033P0;0;8q) + DECGRA ("1;1): Set Raster Attributes
+ w.Write([]byte{0x1b, 0x50, 0x30, 0x3b, 0x30, 0x3b, 0x38, 0x71, 0x22, 0x31, 0x3b, 0x31})
+
+ for n, v := range paletted.Palette {
+ r, g, b, _ := v.RGBA()
+ r = r * 100 / 0xFFFF
+ g = g * 100 / 0xFFFF
+ b = b * 100 / 0xFFFF
+ // DECGCI (#): Graphics Color Introducer
+ fmt.Fprintf(w, "#%d;2;%d;%d;%d", n+1, r, g, b)
+ }
+
+ buf := make([]byte, width*nc)
+ cset := make([]bool, nc)
+ ch0 := specialChNr
+ for z := 0; z < (height+5)/6; z++ {
+ // DECGNL (-): Graphics Next Line
+ if z > 0 {
+ w.Write([]byte{0x2d})
+ }
+ for p := 0; p < 6; p++ {
+ y := z*6 + p
+ for x := 0; x < width; x++ {
+ _, _, _, alpha := img.At(x, y).RGBA()
+ if alpha != 0 {
+ idx := paletted.ColorIndexAt(x, y) + 1
+ cset[idx] = false // mark as used
+ buf[width*int(idx)+x] |= 1 << uint(p)
+ }
+ }
+ }
+ for n := 1; n < nc; n++ {
+ if cset[n] {
+ continue
+ }
+ cset[n] = true
+ // DECGCR ($): Graphics Carriage Return
+ if ch0 == specialChCr {
+ w.Write([]byte{0x24})
+ }
+ // select color (#%d)
+ if n >= 100 {
+ digit1 := n / 100
+ digit2 := (n - digit1*100) / 10
+ digit3 := n % 10
+ c1 := byte(0x30 + digit1)
+ c2 := byte(0x30 + digit2)
+ c3 := byte(0x30 + digit3)
+ w.Write([]byte{0x23, c1, c2, c3})
+ } else if n >= 10 {
+ c1 := byte(0x30 + n/10)
+ c2 := byte(0x30 + n%10)
+ w.Write([]byte{0x23, c1, c2})
+ } else {
+ w.Write([]byte{0x23, byte(0x30 + n)})
+ }
+ cnt := 0
+ for x := 0; x < width; x++ {
+ // make sixel character from 6 pixels
+ ch := buf[width*n+x]
+ buf[width*n+x] = 0
+ if ch0 < 0x40 && ch != ch0 {
+ // output sixel character
+ s := 63 + ch0
+ for ; cnt > 255; cnt -= 255 {
+ w.Write([]byte{0x21, 0x32, 0x35, 0x35, s})
+ }
+ if cnt == 1 {
+ w.Write([]byte{s})
+ } else if cnt == 2 {
+ w.Write([]byte{s, s})
+ } else if cnt == 3 {
+ w.Write([]byte{s, s, s})
+ } else if cnt >= 100 {
+ digit1 := cnt / 100
+ digit2 := (cnt - digit1*100) / 10
+ digit3 := cnt % 10
+ c1 := byte(0x30 + digit1)
+ c2 := byte(0x30 + digit2)
+ c3 := byte(0x30 + digit3)
+ // DECGRI (!): - Graphics Repeat Introducer
+ w.Write([]byte{0x21, c1, c2, c3, s})
+ } else if cnt >= 10 {
+ c1 := byte(0x30 + cnt/10)
+ c2 := byte(0x30 + cnt%10)
+ // DECGRI (!): - Graphics Repeat Introducer
+ w.Write([]byte{0x21, c1, c2, s})
+ } else if cnt > 0 {
+ // DECGRI (!): - Graphics Repeat Introducer
+ w.Write([]byte{0x21, byte(0x30 + cnt), s})
+ }
+ cnt = 0
+ }
+ ch0 = ch
+ cnt++
+ }
+ if ch0 != 0 {
+ // output sixel character
+ s := 63 + ch0
+ for ; cnt > 255; cnt -= 255 {
+ w.Write([]byte{0x21, 0x32, 0x35, 0x35, s})
+ }
+ if cnt == 1 {
+ w.Write([]byte{s})
+ } else if cnt == 2 {
+ w.Write([]byte{s, s})
+ } else if cnt == 3 {
+ w.Write([]byte{s, s, s})
+ } else if cnt >= 100 {
+ digit1 := cnt / 100
+ digit2 := (cnt - digit1*100) / 10
+ digit3 := cnt % 10
+ c1 := byte(0x30 + digit1)
+ c2 := byte(0x30 + digit2)
+ c3 := byte(0x30 + digit3)
+ // DECGRI (!): - Graphics Repeat Introducer
+ w.Write([]byte{0x21, c1, c2, c3, s})
+ } else if cnt >= 10 {
+ c1 := byte(0x30 + cnt/10)
+ c2 := byte(0x30 + cnt%10)
+ // DECGRI (!): - Graphics Repeat Introducer
+ w.Write([]byte{0x21, c1, c2, s})
+ } else if cnt > 0 {
+ // DECGRI (!): - Graphics Repeat Introducer
+ w.Write([]byte{0x21, byte(0x30 + cnt), s})
+ }
+ }
+ ch0 = specialChCr
+ }
+ }
+ // string terminator(ST)
+ w.Write([]byte{0x1b, 0x5c})
+
+ // copy to given buffer
+ if _, ok := e.w.(*os.File); ok {
+ w.(*bytes.Buffer).WriteTo(e.w)
+ }
+
+ return nil
+}
+
+// Decoder decode sixel format into image
+type Decoder struct {
+ r io.Reader
+}
+
+// NewDecoder return new instance of Decoder
+func NewDecoder(r io.Reader) *Decoder {
+ return &Decoder{r}
+}
+
+// Decode do decoding from image
+func (e *Decoder) Decode(img *image.Image) error {
+ buf := bufio.NewReader(e.r)
+ c, err := buf.ReadByte()
+ if err != nil {
+ if err == io.EOF {
+ err = nil
+ }
+ return err
+ }
+ if c != '\x1B' {
+ return errors.New("Invalid format")
+ }
+ c, err = buf.ReadByte()
+ if err != nil {
+ return err
+ }
+ switch c {
+ case 'P':
+ s, err := buf.ReadString('q')
+ if err != nil {
+ return err
+ }
+ s = s[:len(s)-1]
+ tok := strings.Split(s, ";")
+ if len(tok) != 3 {
+ return errors.New("invalid format: illegal header tokens")
+ }
+ default:
+ return errors.New("Invalid format: illegal header")
+ }
+ c, err = buf.ReadByte()
+ if err != nil {
+ return err
+ }
+ if c == '"' {
+ s, err := buf.ReadString('#')
+ if err != nil {
+ return err
+ }
+ tok := strings.Split(s, ";")
+ if len(tok) != 2 {
+ return errors.New("invalid format: illegal size tokens")
+ }
+ err = buf.UnreadByte()
+ if err != nil {
+ return err
+ }
+ } else {
+ err = buf.UnreadByte()
+ if err != nil {
+ return err
+ }
+ }
+
+ colors := map[uint]color.Color{}
+ dx, dy := 0, 0
+ dw, dh, w, h := 0, 0, 200, 200
+ pimg := image.NewNRGBA(image.Rect(0, 0, w, h))
+ var tmp *image.NRGBA
+data:
+ for {
+ c, err = buf.ReadByte()
+ if err != nil {
+ if err == io.EOF {
+ err = nil
+ }
+ return err
+ }
+ if c == '\r' || c == '\n' || c == '\b' {
+ continue
+ }
+ switch {
+ case c == '\x1b':
+ c, err = buf.ReadByte()
+ if err != nil {
+ return err
+ }
+ if c == '\\' {
+ break data
+ }
+ case c == '$':
+ dx = 0
+ case c == '?':
+ pimg.SetNRGBA(dx, dy, color.NRGBA{0, 0, 0, 0})
+ dx++
+ if dx >= dw {
+ dw = dx
+ }
+ case c == '!':
+ err = buf.UnreadByte()
+ if err != nil {
+ return err
+ }
+ var nc, c uint
+ n, err := fmt.Fscanf(buf, "!%d%c", &nc, &c)
+ if err != nil {
+ return err
+ }
+ if n != 2 {
+ return errors.New("invalid format: illegal data tokens")
+ }
+ if c == '?' {
+ }
+ case c == '-':
+ dy++
+ if dy >= dh {
+ dh = dy
+ }
+ case c == '#':
+ err = buf.UnreadByte()
+ if err != nil {
+ return err
+ }
+ var nc, ci uint
+ var r, g, b uint
+ var c byte
+ n, err := fmt.Fscanf(buf, "#%d%c", &nc, &c)
+ if err != nil {
+ return err
+ }
+ if n != 2 {
+ return errors.New("invalid format: illegal data tokens")
+ }
+ if c == ';' {
+ n, err := fmt.Fscanf(buf, "%d;%d;%d;%d", &ci, &r, &g, &b)
+ if err != nil {
+ return err
+ }
+ if n != 4 {
+ return errors.New("invalid format: illegal data tokens")
+ }
+ colors[uint(nc)] = color.NRGBA{uint8(r * 0xFF / 100), uint8(g * 0xFF / 100), uint8(b * 0xFF / 100), 0XFF}
+ } else {
+ err = buf.UnreadByte()
+ if err != nil {
+ return err
+ }
+ if int(nc) < len(colors) {
+ pimg.Set(dx, dy, colors[nc])
+ }
+ dx++
+ if dx >= dw {
+ dw = dx
+ }
+ }
+ default:
+ if c >= '?' && c <= '~' {
+ break
+ }
+ return errors.New("invalid format: illegal data tokens")
+ }
+ if dw > w || dh > h {
+ if dw > w {
+ w *= 2
+ }
+ if dh > h {
+ h *= 2
+ }
+ tmp = image.NewNRGBA(image.Rect(0, 0, w, h))
+ draw.Draw(tmp, pimg.Bounds(), pimg, image.Point{0, 0}, draw.Src)
+ pimg = tmp
+ }
+ }
+ rect := image.Rect(0, 0, dw, dh)
+ tmp = image.NewNRGBA(rect)
+ draw.Draw(tmp, rect, pimg, image.Point{0, 0}, draw.Src)
+ *img = tmp
+ return nil
+}
diff --git a/vendor/github.com/soniakeys/quant/.gitignore b/vendor/github.com/soniakeys/quant/.gitignore
new file mode 100644
index 0000000..e33609d
--- /dev/null
+++ b/vendor/github.com/soniakeys/quant/.gitignore
@@ -0,0 +1 @@
+*.png
diff --git a/vendor/github.com/soniakeys/quant/changelog.md b/vendor/github.com/soniakeys/quant/changelog.md
new file mode 100644
index 0000000..66af50c
--- /dev/null
+++ b/vendor/github.com/soniakeys/quant/changelog.md
@@ -0,0 +1,16 @@
+## v0.2 2013 Nov 14
+
+* Ditherer added. Sierra 24A, a simpler kernel than Floyd-Steinberg.
+Provided through the new draw.Drawer interface which is new to Go 1.2.
+* New draw.Quantizer interface supported, which is also new to Go 1.2.
+* Tests added. These work on whatever .png files are found in the source
+directory. The tests do nothing if no suitable .png files are present.
+
+## v0.1 2013 Sep 20
+
+This started as a little toy program to implement color quantization. But then
+I looked to see what else was published in Go along these lines and didn't find
+much. To add something of interest, I implemented a second algorithm and added
+an interface. That's about all that is in v0.1 here. It's far from general
+utility. It needs things like dithering, subsampling, optimization for common
+image types, and test code.
diff --git a/vendor/github.com/soniakeys/quant/internal/internal.go b/vendor/github.com/soniakeys/quant/internal/internal.go
new file mode 100644
index 0000000..3ff680e
--- /dev/null
+++ b/vendor/github.com/soniakeys/quant/internal/internal.go
@@ -0,0 +1,22 @@
+// Copyright 2013 Sonia Keys.
+// Licensed under MIT license. See "license" file in this source tree.
+
+package internal
+
+import "image"
+
+// PxRGBAfunc returns function to get RGBA color values at (x, y) coordinates of
+// image img. Returned function works the same as img.At(x, y).RGBA() but
+// implements special cases for certain image types to use type-specific methods
+// bypassing color.Color interface which escapes to the heap.
+func PxRGBAfunc(img image.Image) func(x, y int) (r, g, b, a uint32) {
+ switch img0 := img.(type) {
+ case *image.RGBA:
+ return func(x, y int) (r, g, b, a uint32) { return img0.RGBAAt(x, y).RGBA() }
+ case *image.NRGBA:
+ return func(x, y int) (r, g, b, a uint32) { return img0.NRGBAAt(x, y).RGBA() }
+ case *image.YCbCr:
+ return func(x, y int) (r, g, b, a uint32) { return img0.YCbCrAt(x, y).RGBA() }
+ }
+ return func(x, y int) (r, g, b, a uint32) { return img.At(x, y).RGBA() }
+}
diff --git a/vendor/github.com/soniakeys/quant/license b/vendor/github.com/soniakeys/quant/license
new file mode 100644
index 0000000..2a22521
--- /dev/null
+++ b/vendor/github.com/soniakeys/quant/license
@@ -0,0 +1,21 @@
+MIT License:
+
+Copyright (c) 2013 Sonia Keys
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/github.com/soniakeys/quant/median/median.go b/vendor/github.com/soniakeys/quant/median/median.go
new file mode 100644
index 0000000..a8a3b2e
--- /dev/null
+++ b/vendor/github.com/soniakeys/quant/median/median.go
@@ -0,0 +1,409 @@
+// Copyright 2013 Sonia Keys.
+// Licensed under MIT license. See "license" file in this source tree.
+
+// Median implements basic median cut color quantization.
+package median
+
+import (
+ "container/heap"
+ "image"
+ "image/color"
+ "image/draw"
+ "math"
+ "sort"
+
+ "github.com/soniakeys/quant"
+ "github.com/soniakeys/quant/internal"
+)
+
+// Quantizer methods implement median cut color quantization.
+//
+// The value is the target number of colors.
+// Methods do not require pointer receivers, simply construct Quantizer
+// objects with a type conversion.
+//
+// The type satisfies both quant.Quantizer and draw.Quantizer interfaces.
+type Quantizer int
+
+var _ quant.Quantizer = Quantizer(0)
+var _ draw.Quantizer = Quantizer(0)
+
+// Paletted performs color quantization and returns a paletted image.
+//
+// Returned is an image.Paletted with no more than q colors. Note though
+// that image.Paletted is limited to 256 colors.
+func (q Quantizer) Paletted(img image.Image) *image.Paletted {
+ n := int(q)
+ if n > 256 {
+ n = 256
+ }
+ qz := newQuantizer(img, n)
+ if n > 1 {
+ qz.cluster() // cluster pixels by color
+ }
+ return qz.paletted() // generate paletted image from clusters
+}
+
+// Palette performs color quantization and returns a quant.Palette object.
+//
+// Returned is a palette with no more than q colors. Q may be > 256.
+func (q Quantizer) Palette(img image.Image) quant.Palette {
+ qz := newQuantizer(img, int(q))
+ if q > 1 {
+ qz.cluster() // cluster pixels by color
+ }
+ return qz.t
+}
+
+// Quantize performs color quantization and returns a color.Palette.
+//
+// Following the behavior documented with the draw.Quantizer interface,
+// "Quantize appends up to cap(p) - len(p) colors to p and returns the
+// updated palette...." This method does not limit the number of colors
+// to 256. Cap(p) or the quantity cap(p) - len(p) may be > 256.
+// Also for this method the value of the Quantizer object is ignored.
+func (Quantizer) Quantize(p color.Palette, m image.Image) color.Palette {
+ n := cap(p) - len(p)
+ qz := newQuantizer(m, n)
+ if n > 1 {
+ qz.cluster() // cluster pixels by color
+ }
+ return p[:len(p)+copy(p[len(p):cap(p)], qz.t.ColorPalette())]
+}
+
+type quantizer struct {
+ img image.Image // original image
+ cs []cluster // len(cs) is the desired number of colors
+ ch chValues // buffer for computing median
+ t quant.TreePalette // root
+
+ pxRGBA func(x, y int) (r, g, b, a uint32) // function to get original image RGBA color values
+}
+
+type point struct{ x, y int32 }
+type chValues []uint16
+type queue []*cluster
+
+type cluster struct {
+ px []point // list of points in the cluster
+ widestCh int // rgb const identifying axis with widest value range
+ // limits of this cluster
+ minR, maxR uint32
+ minG, maxG uint32
+ minB, maxB uint32
+ // true if corresponding value above represents a bound or hull of the
+ // represented color space
+ bMinR, bMaxR bool
+ bMinG, bMaxG bool
+ bMinB, bMaxB bool
+ node *quant.Node // palette node representing this cluster
+}
+
+// indentifiers for RGB channels, or dimensions or axes of RGB color space
+const (
+ rgbR = iota
+ rgbG
+ rgbB
+)
+
+func newQuantizer(img image.Image, nq int) *quantizer {
+ if nq < 1 {
+ return &quantizer{img: img, pxRGBA: internal.PxRGBAfunc(img)}
+ }
+ b := img.Bounds()
+ npx := (b.Max.X - b.Min.X) * (b.Max.Y - b.Min.Y)
+ qz := &quantizer{
+ img: img,
+ ch: make(chValues, npx),
+ cs: make([]cluster, nq),
+ pxRGBA: internal.PxRGBAfunc(img),
+ }
+ // Populate initial cluster with all pixels from image.
+ c := &qz.cs[0]
+ px := make([]point, npx)
+ c.px = px
+ c.node = &quant.Node{}
+ qz.t.Root = c.node
+ c.minR = math.MaxUint32
+ c.minG = math.MaxUint32
+ c.minB = math.MaxUint32
+ c.bMinR = true
+ c.bMinG = true
+ c.bMinB = true
+ c.bMaxR = true
+ c.bMaxG = true
+ c.bMaxB = true
+ i := 0
+ for y := b.Min.Y; y < b.Max.Y; y++ {
+ for x := b.Min.X; x < b.Max.X; x++ {
+ px[i].x = int32(x)
+ px[i].y = int32(y)
+ r, g, b, _ := qz.pxRGBA(x, y)
+ if r < c.minR {
+ c.minR = r
+ }
+ if r > c.maxR {
+ c.maxR = r
+ }
+ if g < c.minG {
+ c.minG = g
+ }
+ if g > c.maxG {
+ c.maxG = g
+ }
+ if b < c.minB {
+ c.minB = b
+ }
+ if b > c.maxB {
+ c.maxB = b
+ }
+ i++
+ }
+ }
+ return qz
+}
+
+// Cluster by repeatedly splitting clusters.
+// Use a heap as priority queue for picking clusters to split.
+// The rule is to spilt the cluster with the most pixels.
+// Terminate when the desired number of clusters has been populated
+// or when clusters cannot be further split.
+func (qz *quantizer) cluster() {
+ pq := new(queue)
+ // Initial cluster. populated at this point, but not analyzed.
+ c := &qz.cs[0]
+ var m uint32
+ i := 1
+ for {
+ // Only enqueue clusters that can be split.
+ if qz.setWidestChannel(c) {
+ heap.Push(pq, c)
+ }
+ // If no clusters have any color variation, mark the end of the
+ // cluster list and quit early.
+ if len(*pq) == 0 {
+ qz.cs = qz.cs[:i]
+ break
+ }
+ s := heap.Pop(pq).(*cluster) // get cluster to split
+ m = qz.medianCut(s)
+ c = &qz.cs[i] // set c to new cluster
+ i++
+ qz.split(s, c, m) // split s into c and s at value m
+ // Normal exit is when all clusters are populated.
+ if i == len(qz.cs) {
+ break
+ }
+ if qz.setWidestChannel(s) {
+ heap.Push(pq, s) // return s to queue
+ }
+ }
+ // set TreePalette total and indexes
+ qz.t.Leaves = i
+ qz.t.Walk(func(leaf *quant.Node, i int) { leaf.Index = i })
+ // compute palette colors
+ for i := range qz.cs {
+ px := qz.cs[i].px
+ // Average values in cluster to get palette color.
+ var rsum, gsum, bsum int64
+ for _, p := range px {
+ r, g, b, _ := qz.pxRGBA(int(p.x), int(p.y))
+ rsum += int64(r)
+ gsum += int64(g)
+ bsum += int64(b)
+ }
+ n64 := int64(len(px))
+ qz.cs[i].node.Color = color.RGBA64{
+ uint16(rsum / n64),
+ uint16(gsum / n64),
+ uint16(bsum / n64),
+ 0xffff,
+ }
+ }
+}
+
+func (q *quantizer) setWidestChannel(c *cluster) bool {
+ // Find extents of color values in each dimension.
+ // (limits in cluster are not good enough here, we want extents as
+ // represented by pixels.)
+ var maxR, maxG, maxB uint32
+ minR := uint32(math.MaxUint32)
+ minG := uint32(math.MaxUint32)
+ minB := uint32(math.MaxUint32)
+ for _, p := range c.px {
+ r, g, b, _ := q.pxRGBA(int(p.x), int(p.y))
+ if r < minR {
+ minR = r
+ }
+ if r > maxR {
+ maxR = r
+ }
+ if g < minG {
+ minG = g
+ }
+ if g > maxG {
+ maxG = g
+ }
+ if b < minB {
+ minB = b
+ }
+ if b > maxB {
+ maxB = b
+ }
+ }
+ // See which color dimension had the widest range.
+ c.widestCh = rgbG
+ min := minG
+ max := maxG
+ if maxR-minR > max-min {
+ c.widestCh = rgbR
+ min = minR
+ max = maxR
+ }
+ if maxB-minB > max-min {
+ c.widestCh = rgbB
+ min = minB
+ max = maxB
+ }
+ return max > min
+}
+
+// Arg c must have value range > 0 in dimension c.widestDim.
+// return value m is guararanteed to split cluster into two non-empty clusters
+// by v < m where v is pixel value of dimension c.Widest.
+func (q *quantizer) medianCut(c *cluster) uint32 {
+ px := c.px
+ ch := q.ch[:len(px)]
+ // Copy values from appropriate color channel to buffer for
+ // computing median.
+ switch c.widestCh {
+ case rgbR:
+ for i, p := range c.px {
+ r, _, _, _ := q.pxRGBA(int(p.x), int(p.y))
+ ch[i] = uint16(r)
+ }
+ case rgbG:
+ for i, p := range c.px {
+ _, g, _, _ := q.pxRGBA(int(p.x), int(p.y))
+ ch[i] = uint16(g)
+ }
+ case rgbB:
+ for i, p := range c.px {
+ _, _, b, _ := q.pxRGBA(int(p.x), int(p.y))
+ ch[i] = uint16(b)
+ }
+ }
+ // Find cut.
+ sort.Sort(ch)
+ m1 := len(ch) / 2 // median
+ if ch[m1] != ch[m1-1] {
+ return uint32(ch[m1])
+ }
+ m2 := m1
+ // Dec m1 until element to left is different.
+ for m1--; m1 > 0 && ch[m1] == ch[m1-1]; m1-- {
+ }
+ // Inc m2 until element to left is different.
+ for m2++; m2 < len(ch) && ch[m2] == ch[m2-1]; m2++ {
+ }
+ // Return value that makes more equitable cut.
+ if m1 > len(ch)-m2 {
+ return uint32(ch[m1])
+ }
+ return uint32(ch[m2])
+}
+
+// split s into c and s at value m
+func (q *quantizer) split(s, c *cluster, m uint32) {
+ *c = *s // copy extent data
+ px := s.px
+ var v uint32
+ i := 0
+ last := len(px) - 1
+ for i <= last {
+ // Get color value in appropriate dimension.
+ r, g, b, _ := q.pxRGBA(int(px[i].x), int(px[i].y))
+ switch s.widestCh {
+ case rgbR:
+ v = r
+ case rgbG:
+ v = g
+ case rgbB:
+ v = b
+ }
+ // Split at m.
+ if v < m {
+ i++
+ } else {
+ px[last], px[i] = px[i], px[last]
+ last--
+ }
+ }
+ // Split the pixel list. s keeps smaller values, c gets larger values.
+ s.px = px[:i]
+ c.px = px[i:]
+ // Split color extent
+ n := s.node
+ switch s.widestCh {
+ case rgbR:
+ s.maxR = m
+ c.minR = m
+ s.bMaxR = false
+ c.bMinR = false
+ n.Type = quant.TSplitR
+ case rgbG:
+ s.maxG = m
+ c.minG = m
+ s.bMaxG = false
+ c.bMinG = false
+ n.Type = quant.TSplitG
+ case rgbB:
+ s.maxB = m
+ c.minB = m
+ s.bMaxB = false
+ c.bMinB = false
+ n.Type = quant.TSplitB
+ }
+ // Split node
+ n.Split = m
+ n.Low = &quant.Node{}
+ n.High = &quant.Node{}
+ s.node, c.node = n.Low, n.High
+}
+
+func (qz *quantizer) paletted() *image.Paletted {
+ cp := qz.t.ColorPalette()
+ pi := image.NewPaletted(qz.img.Bounds(), cp)
+ for i := range qz.cs {
+ x := uint8(qz.cs[i].node.Index)
+ for _, p := range qz.cs[i].px {
+ pi.SetColorIndex(int(p.x), int(p.y), x)
+ }
+ }
+ return pi
+}
+
+// Implement sort.Interface for sort in median algorithm.
+func (c chValues) Len() int { return len(c) }
+func (c chValues) Less(i, j int) bool { return c[i] < c[j] }
+func (c chValues) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
+
+// Implement heap.Interface for priority queue of clusters.
+func (q queue) Len() int { return len(q) }
+
+// Priority is number of pixels in cluster.
+func (q queue) Less(i, j int) bool { return len(q[i].px) > len(q[j].px) }
+func (q queue) Swap(i, j int) {
+ q[i], q[j] = q[j], q[i]
+}
+func (pq *queue) Push(x interface{}) {
+ c := x.(*cluster)
+ *pq = append(*pq, c)
+}
+func (pq *queue) Pop() interface{} {
+ q := *pq
+ n := len(q) - 1
+ c := q[n]
+ *pq = q[:n]
+ return c
+}
diff --git a/vendor/github.com/soniakeys/quant/median/readme.md b/vendor/github.com/soniakeys/quant/median/readme.md
new file mode 100644
index 0000000..7d19b40
--- /dev/null
+++ b/vendor/github.com/soniakeys/quant/median/readme.md
@@ -0,0 +1,4 @@
+Median
+======
+
+Basic median cut color quantization.
diff --git a/vendor/github.com/soniakeys/quant/palette.go b/vendor/github.com/soniakeys/quant/palette.go
new file mode 100644
index 0000000..67c1606
--- /dev/null
+++ b/vendor/github.com/soniakeys/quant/palette.go
@@ -0,0 +1,173 @@
+// Copyright 2013 Sonia Keys.
+// Licensed under MIT license. See "license" file in this source tree.
+
+package quant
+
+import (
+ "image"
+ "image/color"
+)
+
+// Palette is a palette of color.Colors, much like color.Palette of the
+// standard library.
+//
+// It is defined as an interface here to allow more general implementations,
+// presumably ones that maintain some data structure to achieve performance
+// advantages over linear search.
+type Palette interface {
+ Len() int
+ IndexNear(color.Color) int
+ ColorNear(color.Color) color.Color
+ ColorPalette() color.Palette
+}
+
+var _ Palette = LinearPalette{}
+var _ Palette = TreePalette{}
+
+// LinearPalette implements the Palette interface with color.Palette
+// and has no optimizations.
+type LinearPalette struct {
+ color.Palette
+}
+
+// IndexNear returns the palette index of the nearest palette color.
+//
+// It simply wraps color.Palette.Index.
+func (p LinearPalette) IndexNear(c color.Color) int {
+ return p.Palette.Index(c)
+}
+
+// Color near returns the nearest palette color.
+//
+// It simply wraps color.Palette.Convert.
+func (p LinearPalette) ColorNear(c color.Color) color.Color {
+ return p.Palette.Convert(c)
+}
+
+// ColorPalette satisfies interface Palette.
+//
+// It simply returns the internal color.Palette.
+func (p LinearPalette) ColorPalette() color.Palette {
+ return p.Palette
+}
+
+func (p LinearPalette) Len() int { return len(p.Palette) }
+
+// TreePalette implements the Palette interface with a binary tree.
+//
+// XNear methods run in O(log n) time for palette size.
+//
+// Fields are exported for access by quantizer packages. Typical use of
+// TreePalette should be through methods.
+type TreePalette struct {
+ Leaves int
+ Root *Node
+}
+
+func (p TreePalette) Len() int { return p.Leaves }
+
+// Node is a TreePalette node. It is exported for access by quantizer
+// packages and otherwise can be ignored for typical use of this package.
+type Node struct {
+ Type int
+ // for TLeaf
+ Index int
+ Color color.RGBA64
+ // for TSplit
+ Split uint32
+ Low, High *Node
+}
+
+const (
+ TLeaf = iota
+ TSplitR
+ TSplitG
+ TSplitB
+)
+
+// IndexNear returns the index of the nearest palette color.
+func (t TreePalette) IndexNear(c color.Color) (i int) {
+ if t.Root == nil {
+ return -1
+ }
+ t.Search(c, func(leaf *Node) { i = leaf.Index })
+ return
+}
+
+// ColorNear returns the nearest palette color.
+func (t TreePalette) ColorNear(c color.Color) (p color.Color) {
+ if t.Root == nil {
+ return color.RGBA64{0x7fff, 0x7fff, 0x7fff, 0xfff}
+ }
+ t.Search(c, func(leaf *Node) { p = leaf.Color })
+ return
+}
+
+// Search searches for the given color and calls f for the node representing
+// the nearest color.
+func (t TreePalette) Search(c color.Color, f func(leaf *Node)) {
+ r, g, b, _ := c.RGBA()
+ var lt bool
+ var s func(*Node)
+ s = func(n *Node) {
+ switch n.Type {
+ case TLeaf:
+ f(n)
+ return
+ case TSplitR:
+ lt = r < n.Split
+ case TSplitG:
+ lt = g < n.Split
+ case TSplitB:
+ lt = b < n.Split
+ }
+ if lt {
+ s(n.Low)
+ } else {
+ s(n.High)
+ }
+ }
+ s(t.Root)
+}
+
+// ColorPalette returns a color.Palette corresponding to the TreePalette.
+func (t TreePalette) ColorPalette() color.Palette {
+ if t.Root == nil {
+ return nil
+ }
+ p := make(color.Palette, 0, t.Leaves)
+ t.Walk(func(leaf *Node, i int) {
+ p = append(p, leaf.Color)
+ })
+ return p
+}
+
+// Walk walks the TreePalette calling f for each color.
+func (t TreePalette) Walk(f func(leaf *Node, i int)) {
+ i := 0
+ var w func(*Node)
+ w = func(n *Node) {
+ if n.Type == TLeaf {
+ f(n, i)
+ i++
+ return
+ }
+ w(n.Low)
+ w(n.High)
+ }
+ w(t.Root)
+}
+
+func Paletted(p Palette, img image.Image) *image.Paletted {
+ if p.Len() > 256 {
+ return nil
+ }
+ b := img.Bounds()
+ pi := image.NewPaletted(b, p.ColorPalette())
+ for y := b.Min.Y; y < b.Max.Y; y++ {
+ for x := b.Min.X; x < b.Max.X; x++ {
+ pi.SetColorIndex(x, y, uint8(p.IndexNear(img.At(x, y))))
+ }
+ }
+ return pi
+}
diff --git a/vendor/github.com/soniakeys/quant/quant.go b/vendor/github.com/soniakeys/quant/quant.go
new file mode 100644
index 0000000..a7d9456
--- /dev/null
+++ b/vendor/github.com/soniakeys/quant/quant.go
@@ -0,0 +1,16 @@
+// Copyright 2013 Sonia Keys.
+// Licensed under MIT license. See "license" file in this source tree.
+
+// Quant provides an interface for image color quantizers.
+package quant
+
+import "image"
+
+// Quantizer defines a color quantizer for images.
+type Quantizer interface {
+ // Paletted quantizes an image and returns a paletted image.
+ Paletted(image.Image) *image.Paletted
+ // Palette quantizes an image and returns a Palette. Note the return
+ // type is the Palette interface of this package and not image.Palette.
+ Palette(image.Image) Palette
+}
diff --git a/vendor/github.com/soniakeys/quant/readme.md b/vendor/github.com/soniakeys/quant/readme.md
new file mode 100644
index 0000000..0c35c4c
--- /dev/null
+++ b/vendor/github.com/soniakeys/quant/readme.md
@@ -0,0 +1,8 @@
+Quant
+=====
+
+Experiments with color quantizers
+
+Implemented here are two rather simple quantizers and an (also simple) ditherer.
+The quantizers satisfy the draw.Quantizer interface of the standard library.
+The ditherer satisfies the draw.Drawer interface of the standard library.
diff --git a/vendor/github.com/soniakeys/quant/sierra.go b/vendor/github.com/soniakeys/quant/sierra.go
new file mode 100644
index 0000000..83f1e82
--- /dev/null
+++ b/vendor/github.com/soniakeys/quant/sierra.go
@@ -0,0 +1,158 @@
+// Copyright 2013 Sonia Keys.
+// Licensed under MIT license. See "license" file in this source tree.
+
+package quant
+
+import (
+ "image"
+ "image/color"
+ "image/draw"
+ "math"
+)
+
+// Sierra24A satisfies draw.Drawer
+type Sierra24A struct{}
+
+var _ draw.Drawer = Sierra24A{}
+
+// Draw performs error diffusion dithering.
+//
+// This method satisfies the draw.Drawer interface, implementing a dithering
+// filter attributed to Frankie Sierra. It uses the kernel
+//
+// X 2
+// 1 1
+func (d Sierra24A) Draw(dst draw.Image, r image.Rectangle, src image.Image, sp image.Point) {
+ pd, ok := dst.(*image.Paletted)
+ if !ok {
+ // dither211 currently requires a palette
+ draw.Draw(dst, r, src, sp, draw.Src)
+ return
+ }
+ // intersect r with both dst and src bounds, fix up sp.
+ ir := r.Intersect(pd.Bounds()).
+ Intersect(src.Bounds().Add(r.Min.Sub(sp)))
+ if ir.Empty() {
+ return // no work to do.
+ }
+ sp = ir.Min.Sub(r.Min)
+ // get subimage of src
+ sr := ir.Add(sp)
+ if !sr.Eq(src.Bounds()) {
+ s, ok := src.(interface {
+ SubImage(image.Rectangle) image.Image
+ })
+ if !ok {
+ // dither211 currently works on whole images
+ draw.Draw(dst, r, src, sp, draw.Src)
+ return
+ }
+ src = s.SubImage(sr)
+ }
+ // dither211 currently returns a new image, or nil if dithering not
+ // possible.
+ if s := dither211(src, pd.Palette); s != nil {
+ src = s
+ }
+ // this avoids any problem of src dst overlap but it would usually
+ // work to render directly into dst. todo.
+ draw.Draw(dst, r, src, image.Point{}, draw.Src)
+}
+
+// signed color type, no alpha. signed to represent color deltas as well as
+// color values 0-ffff as with colorRGBA64
+type sRGB struct{ r, g, b int32 }
+type sPalette []sRGB
+
+func (p sPalette) index(c sRGB) int {
+ // still the awful linear search
+ i, min := 0, int64(math.MaxInt64)
+ for j, pc := range p {
+ d := int64(c.r) - int64(pc.r)
+ s := d * d
+ d = int64(c.g) - int64(pc.g)
+ s += d * d
+ d = int64(c.b) - int64(pc.b)
+ s += d * d
+ if s < min {
+ min = s
+ i = j
+ }
+ }
+ return i
+}
+
+// currently this is strictly a helper function for Dither211.Draw, so
+// not generalized to use Palette from this package.
+func dither211(i0 image.Image, cp color.Palette) *image.Paletted {
+ if len(cp) > 256 {
+ // representation limit of image.Paletted. a little sketchy to return
+ // nil, but unworkable results are always better than wrong results.
+ return nil
+ }
+ b := i0.Bounds()
+ pi := image.NewPaletted(b, cp)
+ if b.Empty() {
+ return pi // no work to do
+ }
+ sp := make(sPalette, len(cp))
+ for i, c := range cp {
+ r, g, b, _ := c.RGBA()
+ sp[i] = sRGB{int32(r), int32(g), int32(b)}
+ }
+ // afc is adjustd full color. e, rt, dn hold diffused errors.
+ var afc, e, rt sRGB
+ dn := make([]sRGB, b.Dx()+1)
+ for y := b.Min.Y; y < b.Max.Y; y++ {
+ rt = dn[0]
+ dn[0] = sRGB{}
+ for x := b.Min.X; x < b.Max.X; x++ {
+ // full color from original image
+ r0, g0, b0, _ := i0.At(x, y).RGBA()
+ // adjusted full color = original color + diffused error
+ afc.r = int32(r0) + rt.r>>2
+ afc.g = int32(g0) + rt.g>>2
+ afc.b = int32(b0) + rt.b>>2
+ // clipping or clamping is usually explained as necessary
+ // to avoid integer overflow but with palettes that do not
+ // represent the full color space of the image, it is needed
+ // to keep areas of excess color from saturating at palette
+ // limits and bleeding into neighboring areas.
+ if afc.r < 0 {
+ afc.r = 0
+ } else if afc.r > 0xffff {
+ afc.r = 0xffff
+ }
+ if afc.g < 0 {
+ afc.g = 0
+ } else if afc.g > 0xffff {
+ afc.g = 0xffff
+ }
+ if afc.b < 0 {
+ afc.b = 0
+ } else if afc.b > 0xffff {
+ afc.b = 0xffff
+ }
+ // nearest palette entry
+ i := sp.index(afc)
+ // set pixel in destination image
+ pi.SetColorIndex(x, y, uint8(i))
+ // error to be diffused = full color - palette color.
+ pc := sp[i]
+ e.r = afc.r - pc.r
+ e.g = afc.g - pc.g
+ e.b = afc.b - pc.b
+ // half of error*4 goes right
+ dx := x - b.Min.X + 1
+ rt.r = dn[dx].r + e.r*2
+ rt.g = dn[dx].g + e.g*2
+ rt.b = dn[dx].b + e.b*2
+ // the other half goes down
+ dn[dx] = e
+ dn[dx-1].r += e.r
+ dn[dx-1].g += e.g
+ dn[dx-1].b += e.b
+ }
+ }
+ return pi
+}
diff --git a/vendor/github.com/wcharczuk/go-chart/.gitignore b/vendor/github.com/wcharczuk/go-chart/.gitignore
deleted file mode 100644
index 722d5e7..0000000
--- a/vendor/github.com/wcharczuk/go-chart/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-.vscode
diff --git a/vendor/github.com/wcharczuk/go-chart/.travis.yml b/vendor/github.com/wcharczuk/go-chart/.travis.yml
deleted file mode 100644
index d3fbf34..0000000
--- a/vendor/github.com/wcharczuk/go-chart/.travis.yml
+++ /dev/null
@@ -1,13 +0,0 @@
-language: go
-
-go:
- - 1.6.2
-
-sudo: false
-
-before_script:
- - go get -u github.com/blend/go-sdk/assert
- - go get ./...
-
-script:
- - go test ./...
diff --git a/vendor/github.com/wcharczuk/go-chart/Makefile b/vendor/github.com/wcharczuk/go-chart/Makefile
deleted file mode 100644
index cc16258..0000000
--- a/vendor/github.com/wcharczuk/go-chart/Makefile
+++ /dev/null
@@ -1,9 +0,0 @@
-all: test
-
-test:
- @go test ./...
-
-cover:
- @go test -short -covermode=set -coverprofile=profile.cov
- @go tool cover -html=profile.cov
- @rm profile.cov
\ No newline at end of file
diff --git a/vendor/github.com/wcharczuk/go-chart/market_hours_range.go b/vendor/github.com/wcharczuk/go-chart/market_hours_range.go
deleted file mode 100644
index de32e8c..0000000
--- a/vendor/github.com/wcharczuk/go-chart/market_hours_range.go
+++ /dev/null
@@ -1,195 +0,0 @@
-package chart
-
-import (
- "fmt"
- "time"
-
- "github.com/wcharczuk/go-chart/seq"
- "github.com/wcharczuk/go-chart/util"
-)
-
-// MarketHoursRange is a special type of range that compresses a time range into just the
-// market (i.e. NYSE operating hours and days) range.
-type MarketHoursRange struct {
- Min time.Time
- Max time.Time
-
- MarketOpen time.Time
- MarketClose time.Time
-
- HolidayProvider util.HolidayProvider
-
- ValueFormatter ValueFormatter
-
- Descending bool
- Domain int
-}
-
-// IsDescending returns if the range is descending.
-func (mhr MarketHoursRange) IsDescending() bool {
- return mhr.Descending
-}
-
-// GetTimezone returns the timezone for the market hours range.
-func (mhr MarketHoursRange) GetTimezone() *time.Location {
- return mhr.GetMarketOpen().Location()
-}
-
-// IsZero returns if the range is setup or not.
-func (mhr MarketHoursRange) IsZero() bool {
- return mhr.Min.IsZero() && mhr.Max.IsZero()
-}
-
-// GetMin returns the min value.
-func (mhr MarketHoursRange) GetMin() float64 {
- return util.Time.ToFloat64(mhr.Min)
-}
-
-// GetMax returns the max value.
-func (mhr MarketHoursRange) GetMax() float64 {
- return util.Time.ToFloat64(mhr.GetEffectiveMax())
-}
-
-// GetEffectiveMax gets either the close on the max, or the max itself.
-func (mhr MarketHoursRange) GetEffectiveMax() time.Time {
- maxClose := util.Date.On(mhr.MarketClose, mhr.Max)
- if maxClose.After(mhr.Max) {
- return maxClose
- }
- return mhr.Max
-}
-
-// SetMin sets the min value.
-func (mhr *MarketHoursRange) SetMin(min float64) {
- mhr.Min = util.Time.FromFloat64(min)
- mhr.Min = mhr.Min.In(mhr.GetTimezone())
-}
-
-// SetMax sets the max value.
-func (mhr *MarketHoursRange) SetMax(max float64) {
- mhr.Max = util.Time.FromFloat64(max)
- mhr.Max = mhr.Max.In(mhr.GetTimezone())
-}
-
-// GetDelta gets the delta.
-func (mhr MarketHoursRange) GetDelta() float64 {
- min := mhr.GetMin()
- max := mhr.GetMax()
- return max - min
-}
-
-// GetDomain gets the domain.
-func (mhr MarketHoursRange) GetDomain() int {
- return mhr.Domain
-}
-
-// SetDomain sets the domain.
-func (mhr *MarketHoursRange) SetDomain(domain int) {
- mhr.Domain = domain
-}
-
-// GetHolidayProvider coalesces a userprovided holiday provider and the date.DefaultHolidayProvider.
-func (mhr MarketHoursRange) GetHolidayProvider() util.HolidayProvider {
- if mhr.HolidayProvider == nil {
- return func(_ time.Time) bool { return false }
- }
- return mhr.HolidayProvider
-}
-
-// GetMarketOpen returns the market open time.
-func (mhr MarketHoursRange) GetMarketOpen() time.Time {
- if mhr.MarketOpen.IsZero() {
- return util.NYSEOpen()
- }
- return mhr.MarketOpen
-}
-
-// GetMarketClose returns the market close time.
-func (mhr MarketHoursRange) GetMarketClose() time.Time {
- if mhr.MarketClose.IsZero() {
- return util.NYSEClose()
- }
- return mhr.MarketClose
-}
-
-// GetTicks returns the ticks for the range.
-// This is to override the default continous ticks that would be generated for the range.
-func (mhr *MarketHoursRange) GetTicks(r Renderer, defaults Style, vf ValueFormatter) []Tick {
- times := seq.Time.MarketHours(mhr.Min, mhr.Max, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.GetHolidayProvider())
- timesWidth := mhr.measureTimes(r, defaults, vf, times)
- if timesWidth <= mhr.Domain {
- return mhr.makeTicks(vf, times)
- }
-
- times = seq.Time.MarketHourQuarters(mhr.Min, mhr.Max, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.GetHolidayProvider())
- timesWidth = mhr.measureTimes(r, defaults, vf, times)
- if timesWidth <= mhr.Domain {
- return mhr.makeTicks(vf, times)
- }
-
- times = seq.Time.MarketDayCloses(mhr.Min, mhr.Max, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.GetHolidayProvider())
- timesWidth = mhr.measureTimes(r, defaults, vf, times)
- if timesWidth <= mhr.Domain {
- return mhr.makeTicks(vf, times)
- }
-
- times = seq.Time.MarketDayAlternateCloses(mhr.Min, mhr.Max, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.GetHolidayProvider())
- timesWidth = mhr.measureTimes(r, defaults, vf, times)
- if timesWidth <= mhr.Domain {
- return mhr.makeTicks(vf, times)
- }
-
- times = seq.Time.MarketDayMondayCloses(mhr.Min, mhr.Max, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.GetHolidayProvider())
- timesWidth = mhr.measureTimes(r, defaults, vf, times)
- if timesWidth <= mhr.Domain {
- return mhr.makeTicks(vf, times)
- }
-
- return GenerateContinuousTicks(r, mhr, false, defaults, vf)
-
-}
-
-func (mhr *MarketHoursRange) measureTimes(r Renderer, defaults Style, vf ValueFormatter, times []time.Time) int {
- defaults.GetTextOptions().WriteToRenderer(r)
- var total int
- for index, t := range times {
- timeLabel := vf(t)
-
- labelBox := r.MeasureText(timeLabel)
- total += labelBox.Width()
- if index > 0 {
- total += DefaultMinimumTickHorizontalSpacing
- }
- }
- return total
-}
-
-func (mhr *MarketHoursRange) makeTicks(vf ValueFormatter, times []time.Time) []Tick {
- ticks := make([]Tick, len(times))
- for index, t := range times {
- ticks[index] = Tick{
- Value: util.Time.ToFloat64(t),
- Label: vf(t),
- }
- }
- return ticks
-}
-
-func (mhr MarketHoursRange) String() string {
- return fmt.Sprintf("MarketHoursRange [%s, %s] => %d", mhr.Min.Format(time.RFC3339), mhr.Max.Format(time.RFC3339), mhr.Domain)
-}
-
-// Translate maps a given value into the ContinuousRange space.
-func (mhr MarketHoursRange) Translate(value float64) int {
- valueTime := util.Time.FromFloat64(value)
- valueTimeEastern := valueTime.In(util.Date.Eastern())
- totalSeconds := util.Date.CalculateMarketSecondsBetween(mhr.Min, mhr.GetEffectiveMax(), mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.HolidayProvider)
- valueDelta := util.Date.CalculateMarketSecondsBetween(mhr.Min, valueTimeEastern, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.HolidayProvider)
- translated := int((float64(valueDelta) / float64(totalSeconds)) * float64(mhr.Domain))
-
- if mhr.IsDescending() {
- return mhr.Domain - translated
- }
-
- return translated
-}
diff --git a/vendor/github.com/wcharczuk/go-chart/seq/linear.go b/vendor/github.com/wcharczuk/go-chart/seq/linear.go
deleted file mode 100644
index 699a5ac..0000000
--- a/vendor/github.com/wcharczuk/go-chart/seq/linear.go
+++ /dev/null
@@ -1,73 +0,0 @@
-package seq
-
-// Range returns the array values of a linear seq with a given start, end and optional step.
-func Range(start, end float64) []float64 {
- return Seq{NewLinear().WithStart(start).WithEnd(end).WithStep(1.0)}.Array()
-}
-
-// RangeWithStep returns the array values of a linear seq with a given start, end and optional step.
-func RangeWithStep(start, end, step float64) []float64 {
- return Seq{NewLinear().WithStart(start).WithEnd(end).WithStep(step)}.Array()
-}
-
-// NewLinear returns a new linear generator.
-func NewLinear() *Linear {
- return &Linear{step: 1.0}
-}
-
-// Linear is a stepwise generator.
-type Linear struct {
- start float64
- end float64
- step float64
-}
-
-// Start returns the start value.
-func (lg Linear) Start() float64 {
- return lg.start
-}
-
-// End returns the end value.
-func (lg Linear) End() float64 {
- return lg.end
-}
-
-// Step returns the step value.
-func (lg Linear) Step() float64 {
- return lg.step
-}
-
-// Len returns the number of elements in the seq.
-func (lg Linear) Len() int {
- if lg.start < lg.end {
- return int((lg.end-lg.start)/lg.step) + 1
- }
- return int((lg.start-lg.end)/lg.step) + 1
-}
-
-// GetValue returns the value at a given index.
-func (lg Linear) GetValue(index int) float64 {
- fi := float64(index)
- if lg.start < lg.end {
- return lg.start + (fi * lg.step)
- }
- return lg.start - (fi * lg.step)
-}
-
-// WithStart sets the start and returns the linear generator.
-func (lg *Linear) WithStart(start float64) *Linear {
- lg.start = start
- return lg
-}
-
-// WithEnd sets the end and returns the linear generator.
-func (lg *Linear) WithEnd(end float64) *Linear {
- lg.end = end
- return lg
-}
-
-// WithStep sets the step and returns the linear generator.
-func (lg *Linear) WithStep(step float64) *Linear {
- lg.step = step
- return lg
-}
diff --git a/vendor/github.com/wcharczuk/go-chart/seq/time.go b/vendor/github.com/wcharczuk/go-chart/seq/time.go
deleted file mode 100644
index 79ef02a..0000000
--- a/vendor/github.com/wcharczuk/go-chart/seq/time.go
+++ /dev/null
@@ -1,171 +0,0 @@
-package seq
-
-import (
- "time"
-
- "github.com/wcharczuk/go-chart/util"
-)
-
-// Time is a utility singleton with helper functions for time seq generation.
-var Time timeSequence
-
-type timeSequence struct{}
-
-// Days generates a seq of timestamps by day, from -days to today.
-func (ts timeSequence) Days(days int) []time.Time {
- var values []time.Time
- for day := days; day >= 0; day-- {
- values = append(values, time.Now().AddDate(0, 0, -day))
- }
- return values
-}
-
-func (ts timeSequence) MarketHours(from, to time.Time, marketOpen, marketClose time.Time, isHoliday util.HolidayProvider) []time.Time {
- var times []time.Time
- cursor := util.Date.On(marketOpen, from)
- toClose := util.Date.On(marketClose, to)
- for cursor.Before(toClose) || cursor.Equal(toClose) {
- todayOpen := util.Date.On(marketOpen, cursor)
- todayClose := util.Date.On(marketClose, cursor)
- isValidTradingDay := !isHoliday(cursor) && util.Date.IsWeekDay(cursor.Weekday())
-
- if (cursor.Equal(todayOpen) || cursor.After(todayOpen)) && (cursor.Equal(todayClose) || cursor.Before(todayClose)) && isValidTradingDay {
- times = append(times, cursor)
- }
- if cursor.After(todayClose) {
- cursor = util.Date.NextMarketOpen(cursor, marketOpen, isHoliday)
- } else {
- cursor = util.Date.NextHour(cursor)
- }
- }
- return times
-}
-
-func (ts timeSequence) MarketHourQuarters(from, to time.Time, marketOpen, marketClose time.Time, isHoliday util.HolidayProvider) []time.Time {
- var times []time.Time
- cursor := util.Date.On(marketOpen, from)
- toClose := util.Date.On(marketClose, to)
- for cursor.Before(toClose) || cursor.Equal(toClose) {
-
- isValidTradingDay := !isHoliday(cursor) && util.Date.IsWeekDay(cursor.Weekday())
-
- if isValidTradingDay {
- todayOpen := util.Date.On(marketOpen, cursor)
- todayNoon := util.Date.NoonOn(cursor)
- today2pm := util.Date.On(util.Date.Time(14, 0, 0, 0, cursor.Location()), cursor)
- todayClose := util.Date.On(marketClose, cursor)
- times = append(times, todayOpen, todayNoon, today2pm, todayClose)
- }
-
- cursor = util.Date.NextDay(cursor)
- }
- return times
-}
-
-func (ts timeSequence) MarketDayCloses(from, to time.Time, marketOpen, marketClose time.Time, isHoliday util.HolidayProvider) []time.Time {
- var times []time.Time
- cursor := util.Date.On(marketOpen, from)
- toClose := util.Date.On(marketClose, to)
- for cursor.Before(toClose) || cursor.Equal(toClose) {
- isValidTradingDay := !isHoliday(cursor) && util.Date.IsWeekDay(cursor.Weekday())
- if isValidTradingDay {
- todayClose := util.Date.On(marketClose, cursor)
- times = append(times, todayClose)
- }
-
- cursor = util.Date.NextDay(cursor)
- }
- return times
-}
-
-func (ts timeSequence) MarketDayAlternateCloses(from, to time.Time, marketOpen, marketClose time.Time, isHoliday util.HolidayProvider) []time.Time {
- var times []time.Time
- cursor := util.Date.On(marketOpen, from)
- toClose := util.Date.On(marketClose, to)
- for cursor.Before(toClose) || cursor.Equal(toClose) {
- isValidTradingDay := !isHoliday(cursor) && util.Date.IsWeekDay(cursor.Weekday())
- if isValidTradingDay {
- todayClose := util.Date.On(marketClose, cursor)
- times = append(times, todayClose)
- }
-
- cursor = cursor.AddDate(0, 0, 2)
- }
- return times
-}
-
-func (ts timeSequence) MarketDayMondayCloses(from, to time.Time, marketOpen, marketClose time.Time, isHoliday util.HolidayProvider) []time.Time {
- var times []time.Time
- cursor := util.Date.On(marketClose, from)
- toClose := util.Date.On(marketClose, to)
-
- for cursor.Equal(toClose) || cursor.Before(toClose) {
- isValidTradingDay := !isHoliday(cursor) && util.Date.IsWeekDay(cursor.Weekday())
- if isValidTradingDay {
- times = append(times, cursor)
- }
- cursor = util.Date.NextDayOfWeek(cursor, time.Monday)
- }
- return times
-}
-
-func (ts timeSequence) Hours(start time.Time, totalHours int) []time.Time {
- times := make([]time.Time, totalHours)
-
- last := start
- for i := 0; i < totalHours; i++ {
- times[i] = last
- last = last.Add(time.Hour)
- }
-
- return times
-}
-
-// HoursFilled adds zero values for the data bounded by the start and end of the xdata array.
-func (ts timeSequence) HoursFilled(xdata []time.Time, ydata []float64) ([]time.Time, []float64) {
- start := Time.Start(xdata)
- end := Time.End(xdata)
-
- totalHours := util.Math.AbsInt(util.Date.DiffHours(start, end))
-
- finalTimes := ts.Hours(start, totalHours+1)
- finalValues := make([]float64, totalHours+1)
-
- var hoursFromStart int
- for i, xd := range xdata {
- hoursFromStart = util.Date.DiffHours(start, xd)
- finalValues[hoursFromStart] = ydata[i]
- }
-
- return finalTimes, finalValues
-}
-
-// Start returns the earliest (min) time in a list of times.
-func (ts timeSequence) Start(times []time.Time) time.Time {
- if len(times) == 0 {
- return time.Time{}
- }
-
- start := times[0]
- for _, t := range times[1:] {
- if t.Before(start) {
- start = t
- }
- }
- return start
-}
-
-// Start returns the earliest (min) time in a list of times.
-func (ts timeSequence) End(times []time.Time) time.Time {
- if len(times) == 0 {
- return time.Time{}
- }
-
- end := times[0]
- for _, t := range times[1:] {
- if t.After(end) {
- end = t
- }
- }
- return end
-}
diff --git a/vendor/github.com/wcharczuk/go-chart/seq/util.go b/vendor/github.com/wcharczuk/go-chart/seq/util.go
deleted file mode 100644
index 685a408..0000000
--- a/vendor/github.com/wcharczuk/go-chart/seq/util.go
+++ /dev/null
@@ -1,32 +0,0 @@
-package seq
-
-import "math"
-
-func round(input float64, places int) (rounded float64) {
- if math.IsNaN(input) {
- return 0.0
- }
-
- sign := 1.0
- if input < 0 {
- sign = -1
- input *= -1
- }
-
- precision := math.Pow(10, float64(places))
- digit := input * precision
- _, decimal := math.Modf(digit)
-
- if decimal >= 0.5 {
- rounded = math.Ceil(digit)
- } else {
- rounded = math.Floor(digit)
- }
-
- return rounded / precision * sign
-}
-
-func f64i(value float64) int {
- r := round(value, 0)
- return int(r)
-}
diff --git a/vendor/github.com/wcharczuk/go-chart/util/date.go b/vendor/github.com/wcharczuk/go-chart/util/date.go
deleted file mode 100644
index 18b8603..0000000
--- a/vendor/github.com/wcharczuk/go-chart/util/date.go
+++ /dev/null
@@ -1,396 +0,0 @@
-package util
-
-import (
- "sync"
- "time"
-)
-
-const (
- // AllDaysMask is a bitmask of all the days of the week.
- AllDaysMask = 1<friday.
-func (d date) IsWeekDay(day time.Weekday) bool {
- return !d.IsWeekendDay(day)
-}
-
-// IsWeekendDay returns if the day is a monday->friday.
-func (d date) IsWeekendDay(day time.Weekday) bool {
- return day == time.Saturday || day == time.Sunday
-}
-
-// Before returns if a timestamp is strictly before another date (ignoring hours, minutes etc.)
-func (d date) Before(before, reference time.Time) bool {
- tzAdjustedBefore := before.In(reference.Location())
- if tzAdjustedBefore.Year() < reference.Year() {
- return true
- }
- if tzAdjustedBefore.Month() < reference.Month() {
- return true
- }
- return tzAdjustedBefore.Year() == reference.Year() && tzAdjustedBefore.Month() == reference.Month() && tzAdjustedBefore.Day() < reference.Day()
-}
-
-// NextMarketOpen returns the next market open after a given time.
-func (d date) NextMarketOpen(after, openTime time.Time, isHoliday HolidayProvider) time.Time {
- afterLocalized := after.In(openTime.Location())
- todaysOpen := d.On(openTime, afterLocalized)
-
- if isHoliday == nil {
- isHoliday = defaultHolidayProvider
- }
-
- todayIsValidTradingDay := d.IsWeekDay(todaysOpen.Weekday()) && !isHoliday(todaysOpen)
-
- if (afterLocalized.Equal(todaysOpen) || afterLocalized.Before(todaysOpen)) && todayIsValidTradingDay {
- return todaysOpen
- }
-
- for cursorDay := 1; cursorDay < 7; cursorDay++ {
- newDay := todaysOpen.AddDate(0, 0, cursorDay)
- isValidTradingDay := d.IsWeekDay(newDay.Weekday()) && !isHoliday(newDay)
- if isValidTradingDay {
- return d.On(openTime, newDay)
- }
- }
- panic("Have exhausted day window looking for next market open.")
-}
-
-// NextMarketClose returns the next market close after a given time.
-func (d date) NextMarketClose(after, closeTime time.Time, isHoliday HolidayProvider) time.Time {
- afterLocalized := after.In(closeTime.Location())
-
- if isHoliday == nil {
- isHoliday = defaultHolidayProvider
- }
-
- todaysClose := d.On(closeTime, afterLocalized)
- if afterLocalized.Before(todaysClose) && d.IsWeekDay(todaysClose.Weekday()) && !isHoliday(todaysClose) {
- return todaysClose
- }
-
- if afterLocalized.Equal(todaysClose) { //rare but it might happen.
- return todaysClose
- }
-
- for cursorDay := 1; cursorDay < 6; cursorDay++ {
- newDay := todaysClose.AddDate(0, 0, cursorDay)
- if d.IsWeekDay(newDay.Weekday()) && !isHoliday(newDay) {
- return d.On(closeTime, newDay)
- }
- }
- panic("Have exhausted day window looking for next market close.")
-}
-
-// CalculateMarketSecondsBetween calculates the number of seconds the market was open between two dates.
-func (d date) CalculateMarketSecondsBetween(start, end, marketOpen, marketClose time.Time, isHoliday HolidayProvider) (seconds int64) {
- startEastern := start.In(d.Eastern())
- endEastern := end.In(d.Eastern())
-
- startMarketOpen := d.On(marketOpen, startEastern)
- startMarketClose := d.On(marketClose, startEastern)
-
- if !d.IsWeekendDay(startMarketOpen.Weekday()) && !isHoliday(startMarketOpen) {
- if (startEastern.Equal(startMarketOpen) || startEastern.After(startMarketOpen)) && startEastern.Before(startMarketClose) {
- if endEastern.Before(startMarketClose) {
- seconds += int64(endEastern.Sub(startEastern) / time.Second)
- } else {
- seconds += int64(startMarketClose.Sub(startEastern) / time.Second)
- }
- }
- }
-
- cursor := d.NextMarketOpen(startMarketClose, marketOpen, isHoliday)
- for d.Before(cursor, endEastern) {
- if d.IsWeekDay(cursor.Weekday()) && !isHoliday(cursor) {
- close := d.NextMarketClose(cursor, marketClose, isHoliday)
- seconds += int64(close.Sub(cursor) / time.Second)
- }
- cursor = cursor.AddDate(0, 0, 1)
- }
-
- finalMarketOpen := d.NextMarketOpen(cursor, marketOpen, isHoliday)
- finalMarketClose := d.NextMarketClose(cursor, marketClose, isHoliday)
- if endEastern.After(finalMarketOpen) {
- if endEastern.Before(finalMarketClose) {
- seconds += int64(endEastern.Sub(finalMarketOpen) / time.Second)
- } else {
- seconds += int64(finalMarketClose.Sub(finalMarketOpen) / time.Second)
- }
- }
-
- return
-}
-
-const (
- _secondsPerHour = 60 * 60
- _secondsPerDay = 60 * 60 * 24
-)
-
-func (d date) DiffDays(t1, t2 time.Time) (days int) {
- t1n := t1.Unix()
- t2n := t2.Unix()
- diff := t2n - t1n //yields seconds
- return int(diff / (_secondsPerDay))
-}
-
-func (d date) DiffHours(t1, t2 time.Time) (hours int) {
- t1n := t1.Unix()
- t2n := t2.Unix()
- diff := t2n - t1n //yields seconds
- return int(diff / (_secondsPerHour))
-}
-
-// NextDay returns the timestamp advanced a day.
-func (d date) NextDay(ts time.Time) time.Time {
- return ts.AddDate(0, 0, 1)
-}
-
-// NextHour returns the next timestamp on the hour.
-func (d date) NextHour(ts time.Time) time.Time {
- //advance a full hour ...
- advanced := ts.Add(time.Hour)
- minutes := time.Duration(advanced.Minute()) * time.Minute
- final := advanced.Add(-minutes)
- return time.Date(final.Year(), final.Month(), final.Day(), final.Hour(), 0, 0, 0, final.Location())
-}
-
-// NextDayOfWeek returns the next instance of a given weekday after a given timestamp.
-func (d date) NextDayOfWeek(after time.Time, dayOfWeek time.Weekday) time.Time {
- afterWeekday := after.Weekday()
- if afterWeekday == dayOfWeek {
- return after.AddDate(0, 0, 7)
- }
-
- // 1 vs 5 ~ add 4 days
- if afterWeekday < dayOfWeek {
- dayDelta := int(dayOfWeek - afterWeekday)
- return after.AddDate(0, 0, dayDelta)
- }
-
- // 5 vs 1, add 7-(5-1) ~ 3 days
- dayDelta := 7 - int(afterWeekday-dayOfWeek)
- return after.AddDate(0, 0, dayDelta)
-}
diff --git a/vendor/github.com/wcharczuk/go-chart/util/date_posix.go b/vendor/github.com/wcharczuk/go-chart/util/date_posix.go
deleted file mode 100644
index 1a5a80c..0000000
--- a/vendor/github.com/wcharczuk/go-chart/util/date_posix.go
+++ /dev/null
@@ -1,17 +0,0 @@
-// +build !windows
-
-package util
-
-import "time"
-
-// Eastern returns the eastern timezone.
-func (d date) Eastern() *time.Location {
- if _eastern == nil {
- _easternLock.Lock()
- defer _easternLock.Unlock()
- if _eastern == nil {
- _eastern, _ = time.LoadLocation("America/New_York")
- }
- }
- return _eastern
-}
diff --git a/vendor/github.com/wcharczuk/go-chart/util/date_windows.go b/vendor/github.com/wcharczuk/go-chart/util/date_windows.go
deleted file mode 100644
index c42a367..0000000
--- a/vendor/github.com/wcharczuk/go-chart/util/date_windows.go
+++ /dev/null
@@ -1,17 +0,0 @@
-// +build windows
-
-package util
-
-import "time"
-
-// Eastern returns the eastern timezone.
-func (d date) Eastern() *time.Location {
- if _eastern == nil {
- _easternLock.Lock()
- defer _easternLock.Unlock()
- if _eastern == nil {
- _eastern, _ = time.LoadLocation("EST")
- }
- }
- return _eastern
-}
diff --git a/vendor/github.com/wcharczuk/go-chart/util/file_util.go b/vendor/github.com/wcharczuk/go-chart/util/file_util.go
deleted file mode 100644
index d6c561d..0000000
--- a/vendor/github.com/wcharczuk/go-chart/util/file_util.go
+++ /dev/null
@@ -1,57 +0,0 @@
-package util
-
-import (
- "bufio"
- "io"
- "os"
-)
-
-var (
- // File contains file utility functions
- File = fileUtil{}
-)
-
-type fileUtil struct{}
-
-// ReadByLines reads a file and calls the handler for each line.
-func (fu fileUtil) ReadByLines(filePath string, handler func(line string) error) error {
- var f *os.File
- var err error
- if f, err = os.Open(filePath); err == nil {
- defer f.Close()
- var line string
- scanner := bufio.NewScanner(f)
- for scanner.Scan() {
- line = scanner.Text()
- err = handler(line)
- if err != nil {
- return err
- }
- }
- }
- return err
-
-}
-
-// ReadByChunks reads a file in `chunkSize` pieces, dispatched to the handler.
-func (fu fileUtil) ReadByChunks(filePath string, chunkSize int, handler func(line []byte) error) error {
- var f *os.File
- var err error
- if f, err = os.Open(filePath); err == nil {
- defer f.Close()
-
- chunk := make([]byte, chunkSize)
- for {
- readBytes, err := f.Read(chunk)
- if err == io.EOF {
- break
- }
- readData := chunk[:readBytes]
- err = handler(readData)
- if err != nil {
- return err
- }
- }
- }
- return err
-}
diff --git a/vendor/github.com/wcharczuk/go-chart/util/time.go b/vendor/github.com/wcharczuk/go-chart/util/time.go
deleted file mode 100644
index 88e0c8b..0000000
--- a/vendor/github.com/wcharczuk/go-chart/util/time.go
+++ /dev/null
@@ -1,20 +0,0 @@
-package util
-
-import "time"
-
-var (
- // Time contains time utility functions.
- Time = timeUtil{}
-)
-
-type timeUtil struct{}
-
-// TimeToFloat64 returns a float64 representation of a time.
-func (tu timeUtil) ToFloat64(t time.Time) float64 {
- return float64(t.UnixNano())
-}
-
-// Float64ToTime returns a time from a float64.
-func (tu timeUtil) FromFloat64(tf float64) time.Time {
- return time.Unix(0, int64(tf))
-}
diff --git a/vendor/github.com/wcharczuk/go-chart/v2/.gitignore b/vendor/github.com/wcharczuk/go-chart/v2/.gitignore
new file mode 100644
index 0000000..3e4b6e1
--- /dev/null
+++ b/vendor/github.com/wcharczuk/go-chart/v2/.gitignore
@@ -0,0 +1,19 @@
+# Binaries for programs and plugins
+*.exe
+*.dll
+*.so
+*.dylib
+
+# Test binary, build with `go test -c`
+*.test
+
+# Output of the go coverage tool, specifically when used with LiteIDE
+*.out
+
+# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
+.glide/
+
+# Other
+.vscode
+.DS_Store
+coverage.html
\ No newline at end of file
diff --git a/vendor/github.com/wcharczuk/go-chart/v2/COVERAGE b/vendor/github.com/wcharczuk/go-chart/v2/COVERAGE
new file mode 100644
index 0000000..7e492f8
--- /dev/null
+++ b/vendor/github.com/wcharczuk/go-chart/v2/COVERAGE
@@ -0,0 +1 @@
+29.02
\ No newline at end of file
diff --git a/vendor/github.com/wcharczuk/go-chart/LICENSE b/vendor/github.com/wcharczuk/go-chart/v2/LICENSE
similarity index 100%
rename from vendor/github.com/wcharczuk/go-chart/LICENSE
rename to vendor/github.com/wcharczuk/go-chart/v2/LICENSE
diff --git a/vendor/github.com/wcharczuk/go-chart/v2/Makefile b/vendor/github.com/wcharczuk/go-chart/v2/Makefile
new file mode 100644
index 0000000..e0928c3
--- /dev/null
+++ b/vendor/github.com/wcharczuk/go-chart/v2/Makefile
@@ -0,0 +1,10 @@
+all: new-install test
+
+new-install:
+ @go get -v -u ./...
+
+generate:
+ @go generate ./...
+
+test:
+ @go test ./...
\ No newline at end of file
diff --git a/vendor/github.com/wcharczuk/go-chart/v2/PROFANITY_RULES.yml b/vendor/github.com/wcharczuk/go-chart/v2/PROFANITY_RULES.yml
new file mode 100644
index 0000000..1e6c803
--- /dev/null
+++ b/vendor/github.com/wcharczuk/go-chart/v2/PROFANITY_RULES.yml
@@ -0,0 +1,4 @@
+go-sdk:
+ excludeFiles: [ "*_test.go" ]
+ importsContain: [ github.com/blend/go-sdk/* ]
+ description: "please don't use go-sdk in this repo"
\ No newline at end of file
diff --git a/vendor/github.com/wcharczuk/go-chart/README.md b/vendor/github.com/wcharczuk/go-chart/v2/README.md
similarity index 66%
rename from vendor/github.com/wcharczuk/go-chart/README.md
rename to vendor/github.com/wcharczuk/go-chart/v2/README.md
index 0ed0a51..a0cdbc0 100644
--- a/vendor/github.com/wcharczuk/go-chart/README.md
+++ b/vendor/github.com/wcharczuk/go-chart/v2/README.md
@@ -1,13 +1,10 @@
go-chart
========
-[](https://travis-ci.org/wcharczuk/go-chart)[](https://goreportcard.com/report/github.com/wcharczuk/go-chart)
+[](https://circleci.com/gh/wcharczuk/go-chart) [](https://goreportcard.com/report/github.com/wcharczuk/go-chart)
-Package `chart` is a very simple golang native charting library that supports timeseries and continuous
-line charts.
+Package `chart` is a very simple golang native charting library that supports timeseries and continuous line charts.
-The v1.0 release has been tagged so things should be more or less stable, if something changes please log an issue.
-
-Master should now be on the v2.x codebase, which brings a couple new features and better handling of basics like axes labeling etc. Per usual, see `_examples` for more information.
+Master should now be on the v3.x codebase, which overhauls the api significantly. Per usual, see `examples` for more information.
# Installation
@@ -17,9 +14,9 @@ To install `chart` run the following:
> go get -u github.com/wcharczuk/go-chart
```
-Most of the components are interchangeable so feel free to crib whatever you want.
+Most of the components are interchangeable so feel free to crib whatever you want.
-# Output Examples
+# Output Examples
Spark Lines:
@@ -39,17 +36,17 @@ Pie Chart:

-The code for this chart can be found in `_examples/pie_chart/main.go`.
+The code for this chart can be found in `examples/pie_chart/main.go`.
Stacked Bar:

-The code for this chart can be found in `_examples/stacked_bar/main.go`.
+The code for this chart can be found in `examples/stacked_bar/main.go`.
# Code Examples
-Actual chart configurations and examples can be found in the `./_examples/` directory. They are web servers, so start them with `go run main.go` then access `http://localhost:8080` to see the output.
+Actual chart configurations and examples can be found in the `./examples/` directory. They are simple CLI programs that write to `output.png` (they are also updated with `go generate`.
# Usage
@@ -83,8 +80,7 @@ Here, we have a single series with x range values as float64s, rendered to a PNG
# API Overview
-Everything on the `chart.Chart` object has defaults that can be overriden. Whenever a developer sets a property on the chart object, it is to be assumed that value will be used instead of the default. One complication here
-is any object's root `chart.Style` object (i.e named `Style`) and the `Show` property specifically, if any other property is set and the `Show` property is unset, it is assumed to be it's default value of `False`.
+Everything on the `chart.Chart` object has defaults that can be overriden. Whenever a developer sets a property on the chart object, it is to be assumed that value will be used instead of the default.
The best way to see the api in action is to look at the examples in the `./_examples/` directory.
@@ -96,4 +92,4 @@ The goal with the API itself is to have the "zero value be useful", and to requi
# Contributions
-This library is super early but contributions are welcome.
+Contributions are welcome though this library is in a holding pattern for the forseable future.
diff --git a/vendor/github.com/wcharczuk/go-chart/annotation_series.go b/vendor/github.com/wcharczuk/go-chart/v2/annotation_series.go
similarity index 86%
rename from vendor/github.com/wcharczuk/go-chart/annotation_series.go
rename to vendor/github.com/wcharczuk/go-chart/v2/annotation_series.go
index 9c383c9..96e78f9 100644
--- a/vendor/github.com/wcharczuk/go-chart/annotation_series.go
+++ b/vendor/github.com/wcharczuk/go-chart/v2/annotation_series.go
@@ -3,8 +3,11 @@ package chart
import (
"fmt"
"math"
+)
- util "github.com/wcharczuk/go-chart/util"
+// Interface Assertions.
+var (
+ _ Series = (*AnnotationSeries)(nil)
)
// AnnotationSeries is a series of labels on the chart.
@@ -50,17 +53,17 @@ func (as AnnotationSeries) Measure(r Renderer, canvasBox Box, xrange, yrange Ran
Right: 0,
Bottom: 0,
}
- if as.Style.IsZero() || as.Style.Show {
+ if !as.Style.Hidden {
seriesStyle := as.Style.InheritFrom(as.annotationStyleDefaults(defaults))
for _, a := range as.Annotations {
style := a.Style.InheritFrom(seriesStyle)
lx := canvasBox.Left + xrange.Translate(a.XValue)
ly := canvasBox.Bottom - yrange.Translate(a.YValue)
ab := Draw.MeasureAnnotation(r, canvasBox, style, lx, ly, a.Label)
- box.Top = util.Math.MinInt(box.Top, ab.Top)
- box.Left = util.Math.MinInt(box.Left, ab.Left)
- box.Right = util.Math.MaxInt(box.Right, ab.Right)
- box.Bottom = util.Math.MaxInt(box.Bottom, ab.Bottom)
+ box.Top = MinInt(box.Top, ab.Top)
+ box.Left = MinInt(box.Left, ab.Left)
+ box.Right = MaxInt(box.Right, ab.Right)
+ box.Bottom = MaxInt(box.Bottom, ab.Bottom)
}
}
return box
@@ -68,7 +71,7 @@ func (as AnnotationSeries) Measure(r Renderer, canvasBox Box, xrange, yrange Ran
// Render draws the series.
func (as AnnotationSeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
- if as.Style.IsZero() || as.Style.Show {
+ if !as.Style.Hidden {
seriesStyle := as.Style.InheritFrom(as.annotationStyleDefaults(defaults))
for _, a := range as.Annotations {
style := a.Style.InheritFrom(seriesStyle)
diff --git a/vendor/github.com/wcharczuk/go-chart/seq/array.go b/vendor/github.com/wcharczuk/go-chart/v2/array.go
similarity index 65%
rename from vendor/github.com/wcharczuk/go-chart/seq/array.go
rename to vendor/github.com/wcharczuk/go-chart/v2/array.go
index 08479c2..71b3ee7 100644
--- a/vendor/github.com/wcharczuk/go-chart/seq/array.go
+++ b/vendor/github.com/wcharczuk/go-chart/v2/array.go
@@ -1,6 +1,11 @@
-package seq
+package chart
-// NewArray creates a new array.
+var (
+ _ Sequence = (*Array)(nil)
+)
+
+// NewArray returns a new array from a given set of values.
+// Array implements Sequence, which allows it to be used with the sequence helpers.
func NewArray(values ...float64) Array {
return Array(values)
}
diff --git a/vendor/github.com/wcharczuk/go-chart/axis.go b/vendor/github.com/wcharczuk/go-chart/v2/axis.go
similarity index 100%
rename from vendor/github.com/wcharczuk/go-chart/axis.go
rename to vendor/github.com/wcharczuk/go-chart/v2/axis.go
diff --git a/vendor/github.com/wcharczuk/go-chart/bar_chart.go b/vendor/github.com/wcharczuk/go-chart/v2/bar_chart.go
similarity index 94%
rename from vendor/github.com/wcharczuk/go-chart/bar_chart.go
rename to vendor/github.com/wcharczuk/go-chart/v2/bar_chart.go
index 0c24d92..d61f3db 100644
--- a/vendor/github.com/wcharczuk/go-chart/bar_chart.go
+++ b/vendor/github.com/wcharczuk/go-chart/v2/bar_chart.go
@@ -7,7 +7,6 @@ import (
"math"
"github.com/golang/freetype/truetype"
- util "github.com/wcharczuk/go-chart/util"
)
// BarChart is a chart that draws bars on a range.
@@ -31,6 +30,9 @@ type BarChart struct {
BarSpacing int
+ UseBaseValue bool
+ BaseValue float64
+
Font *truetype.Font
defaultFont *truetype.Font
@@ -199,11 +201,20 @@ func (bc BarChart) drawBars(r Renderer, canvasBox Box, yr Range) {
by = canvasBox.Bottom - yr.Translate(bar.Value)
- barBox = Box{
- Top: by,
- Left: bxl,
- Right: bxr,
- Bottom: canvasBox.Bottom,
+ if bc.UseBaseValue {
+ barBox = Box{
+ Top: by,
+ Left: bxl,
+ Right: bxr,
+ Bottom: canvasBox.Bottom - yr.Translate(bc.BaseValue),
+ }
+ } else {
+ barBox = Box{
+ Top: by,
+ Left: bxl,
+ Right: bxr,
+ Bottom: canvasBox.Bottom,
+ }
}
Draw.Box(r, barBox, bar.Style.InheritFrom(bc.styleDefaultsBar(index)))
@@ -213,7 +224,7 @@ func (bc BarChart) drawBars(r Renderer, canvasBox Box, yr Range) {
}
func (bc BarChart) drawXAxis(r Renderer, canvasBox Box) {
- if bc.XAxis.Show {
+ if !bc.XAxis.Hidden {
axisStyle := bc.XAxis.InheritFrom(bc.styleDefaultsAxes())
axisStyle.WriteToRenderer(r)
@@ -252,7 +263,7 @@ func (bc BarChart) drawXAxis(r Renderer, canvasBox Box) {
}
func (bc BarChart) drawYAxis(r Renderer, canvasBox Box, yr Range, ticks []Tick) {
- if bc.YAxis.Style.Show {
+ if !bc.YAxis.Style.Hidden {
axisStyle := bc.YAxis.Style.InheritFrom(bc.styleDefaultsAxes())
axisStyle.WriteToRenderer(r)
@@ -283,7 +294,7 @@ func (bc BarChart) drawYAxis(r Renderer, canvasBox Box, yr Range, ticks []Tick)
}
func (bc BarChart) drawTitle(r Renderer) {
- if len(bc.Title) > 0 && bc.TitleStyle.Show {
+ if len(bc.Title) > 0 && !bc.TitleStyle.Hidden {
r.SetFont(bc.TitleStyle.GetFont(bc.GetFont()))
r.SetFontColor(bc.TitleStyle.GetFontColor(bc.GetColorPalette().TextColor()))
titleFontSize := bc.TitleStyle.GetFontSize(bc.getTitleFontSize())
@@ -314,7 +325,7 @@ func (bc BarChart) styleDefaultsCanvas() Style {
}
func (bc BarChart) hasAxes() bool {
- return bc.YAxis.Style.Show
+ return !bc.YAxis.Style.Hidden
}
func (bc BarChart) setRangeDomains(canvasBox Box, yr Range) Range {
@@ -334,7 +345,7 @@ func (bc BarChart) getValueFormatters() ValueFormatter {
}
func (bc BarChart) getAxesTicks(r Renderer, yr Range, yf ValueFormatter) (yticks []Tick) {
- if bc.YAxis.Style.Show {
+ if !bc.YAxis.Style.Hidden {
yticks = bc.YAxis.GetTicks(r, yr, bc.styleDefaultsAxes(), yf)
}
return
@@ -380,7 +391,7 @@ func (bc BarChart) getAdjustedCanvasBox(r Renderer, canvasBox Box, yrange Range,
_, _, totalWidth := bc.calculateScaledTotalWidth(canvasBox)
- if bc.XAxis.Show {
+ if !bc.XAxis.Hidden {
xaxisHeight := DefaultVerticalTickHeight
axisStyle := bc.XAxis.InheritFrom(bc.styleDefaultsAxes())
@@ -398,7 +409,7 @@ func (bc BarChart) getAdjustedCanvasBox(r Renderer, canvasBox Box, yrange Range,
lines := Text.WrapFit(r, bar.Label, barLabelBox.Width(), axisStyle)
linesBox := Text.MeasureLines(r, lines, axisStyle)
- xaxisHeight = util.Math.MinInt(linesBox.Height()+(2*DefaultXAxisMargin), xaxisHeight)
+ xaxisHeight = MinInt(linesBox.Height()+(2*DefaultXAxisMargin), xaxisHeight)
}
}
@@ -412,7 +423,7 @@ func (bc BarChart) getAdjustedCanvasBox(r Renderer, canvasBox Box, yrange Range,
axesOuterBox = axesOuterBox.Grow(xbox)
}
- if bc.YAxis.Style.Show {
+ if !bc.YAxis.Style.Hidden {
axesBounds := bc.YAxis.Measure(r, canvasBox, yrange, bc.styleDefaultsAxes(), yticks)
axesOuterBox = axesOuterBox.Grow(axesBounds)
}
@@ -465,7 +476,7 @@ func (bc BarChart) styleDefaultsTitle() Style {
}
func (bc BarChart) getTitleFontSize() float64 {
- effectiveDimension := util.Math.MinInt(bc.GetWidth(), bc.GetHeight())
+ effectiveDimension := MinInt(bc.GetWidth(), bc.GetHeight())
if effectiveDimension >= 2048 {
return 48
} else if effectiveDimension >= 1024 {
diff --git a/vendor/github.com/wcharczuk/go-chart/bollinger_band_series.go b/vendor/github.com/wcharczuk/go-chart/v2/bollinger_band_series.go
similarity index 89%
rename from vendor/github.com/wcharczuk/go-chart/bollinger_band_series.go
rename to vendor/github.com/wcharczuk/go-chart/v2/bollinger_band_series.go
index 9dbd3b8..728b232 100644
--- a/vendor/github.com/wcharczuk/go-chart/bollinger_band_series.go
+++ b/vendor/github.com/wcharczuk/go-chart/v2/bollinger_band_series.go
@@ -2,8 +2,11 @@ package chart
import (
"fmt"
+)
- "github.com/wcharczuk/go-chart/seq"
+// Interface Assertions.
+var (
+ _ Series = (*BollingerBandsSeries)(nil)
)
// BollingerBandsSeries draws bollinger bands for an inner series.
@@ -17,7 +20,7 @@ type BollingerBandsSeries struct {
K float64
InnerSeries ValuesProvider
- valueBuffer *seq.Buffer
+ valueBuffer *ValueBuffer
}
// GetName returns the name of the time series.
@@ -67,7 +70,7 @@ func (bbs *BollingerBandsSeries) GetBoundedValues(index int) (x, y1, y2 float64)
return
}
if bbs.valueBuffer == nil || index == 0 {
- bbs.valueBuffer = seq.NewBufferWithCapacity(bbs.GetPeriod())
+ bbs.valueBuffer = NewValueBufferWithCapacity(bbs.GetPeriod())
}
if bbs.valueBuffer.Len() >= bbs.GetPeriod() {
bbs.valueBuffer.Dequeue()
@@ -76,8 +79,8 @@ func (bbs *BollingerBandsSeries) GetBoundedValues(index int) (x, y1, y2 float64)
bbs.valueBuffer.Enqueue(py)
x = px
- ay := seq.New(bbs.valueBuffer).Average()
- std := seq.New(bbs.valueBuffer).StdDev()
+ ay := Seq{bbs.valueBuffer}.Average()
+ std := Seq{bbs.valueBuffer}.StdDev()
y1 = ay + (bbs.GetK() * std)
y2 = ay - (bbs.GetK() * std)
@@ -96,15 +99,15 @@ func (bbs *BollingerBandsSeries) GetBoundedLastValues() (x, y1, y2 float64) {
startAt = 0
}
- vb := seq.NewBufferWithCapacity(period)
+ vb := NewValueBufferWithCapacity(period)
for index := startAt; index < seriesLength; index++ {
xn, yn := bbs.InnerSeries.GetValues(index)
vb.Enqueue(yn)
x = xn
}
- ay := seq.Seq{Provider: vb}.Average()
- std := seq.Seq{Provider: vb}.StdDev()
+ ay := Seq{vb}.Average()
+ std := Seq{vb}.StdDev()
y1 = ay + (bbs.GetK() * std)
y2 = ay - (bbs.GetK() * std)
diff --git a/vendor/github.com/wcharczuk/go-chart/v2/bounded_last_values_annotation_series.go b/vendor/github.com/wcharczuk/go-chart/v2/bounded_last_values_annotation_series.go
new file mode 100644
index 0000000..670ddf7
--- /dev/null
+++ b/vendor/github.com/wcharczuk/go-chart/v2/bounded_last_values_annotation_series.go
@@ -0,0 +1,36 @@
+package chart
+
+import "fmt"
+
+// BoundedLastValuesAnnotationSeries returns a last value annotation series for a bounded values provider.
+func BoundedLastValuesAnnotationSeries(innerSeries FullBoundedValuesProvider, vfs ...ValueFormatter) AnnotationSeries {
+ lvx, lvy1, lvy2 := innerSeries.GetBoundedLastValues()
+
+ var vf ValueFormatter
+ if len(vfs) > 0 {
+ vf = vfs[0]
+ } else if typed, isTyped := innerSeries.(ValueFormatterProvider); isTyped {
+ _, vf = typed.GetValueFormatters()
+ } else {
+ vf = FloatValueFormatter
+ }
+
+ label1 := vf(lvy1)
+ label2 := vf(lvy2)
+
+ var seriesName string
+ var seriesStyle Style
+ if typed, isTyped := innerSeries.(Series); isTyped {
+ seriesName = fmt.Sprintf("%s - Last Values", typed.GetName())
+ seriesStyle = typed.GetStyle()
+ }
+
+ return AnnotationSeries{
+ Name: seriesName,
+ Style: seriesStyle,
+ Annotations: []Value2{
+ {XValue: lvx, YValue: lvy1, Label: label1},
+ {XValue: lvx, YValue: lvy2, Label: label2},
+ },
+ }
+}
diff --git a/vendor/github.com/wcharczuk/go-chart/box.go b/vendor/github.com/wcharczuk/go-chart/v2/box.go
similarity index 79%
rename from vendor/github.com/wcharczuk/go-chart/box.go
rename to vendor/github.com/wcharczuk/go-chart/v2/box.go
index c59ab69..9611ff9 100644
--- a/vendor/github.com/wcharczuk/go-chart/box.go
+++ b/vendor/github.com/wcharczuk/go-chart/v2/box.go
@@ -3,8 +3,6 @@ package chart
import (
"fmt"
"math"
-
- util "github.com/wcharczuk/go-chart/util"
)
var (
@@ -91,12 +89,12 @@ func (b Box) GetBottom(defaults ...int) int {
// Width returns the width
func (b Box) Width() int {
- return util.Math.AbsInt(b.Right - b.Left)
+ return AbsInt(b.Right - b.Left)
}
// Height returns the height
func (b Box) Height() int {
- return util.Math.AbsInt(b.Bottom - b.Top)
+ return AbsInt(b.Bottom - b.Top)
}
// Center returns the center of the box
@@ -148,10 +146,10 @@ func (b Box) Equals(other Box) bool {
// Grow grows a box based on another box.
func (b Box) Grow(other Box) Box {
return Box{
- Top: util.Math.MinInt(b.Top, other.Top),
- Left: util.Math.MinInt(b.Left, other.Left),
- Right: util.Math.MaxInt(b.Right, other.Right),
- Bottom: util.Math.MaxInt(b.Bottom, other.Bottom),
+ Top: MinInt(b.Top, other.Top),
+ Left: MinInt(b.Left, other.Left),
+ Right: MaxInt(b.Right, other.Right),
+ Bottom: MaxInt(b.Bottom, other.Bottom),
}
}
@@ -222,10 +220,10 @@ func (b Box) Fit(other Box) Box {
func (b Box) Constrain(other Box) Box {
newBox := b.Clone()
- newBox.Top = util.Math.MaxInt(newBox.Top, other.Top)
- newBox.Left = util.Math.MaxInt(newBox.Left, other.Left)
- newBox.Right = util.Math.MinInt(newBox.Right, other.Right)
- newBox.Bottom = util.Math.MinInt(newBox.Bottom, other.Bottom)
+ newBox.Top = MaxInt(newBox.Top, other.Top)
+ newBox.Left = MaxInt(newBox.Left, other.Left)
+ newBox.Right = MinInt(newBox.Right, other.Right)
+ newBox.Bottom = MinInt(newBox.Bottom, other.Bottom)
return newBox
}
@@ -264,36 +262,36 @@ type BoxCorners struct {
// Box return the BoxCorners as a regular box.
func (bc BoxCorners) Box() Box {
return Box{
- Top: util.Math.MinInt(bc.TopLeft.Y, bc.TopRight.Y),
- Left: util.Math.MinInt(bc.TopLeft.X, bc.BottomLeft.X),
- Right: util.Math.MaxInt(bc.TopRight.X, bc.BottomRight.X),
- Bottom: util.Math.MaxInt(bc.BottomLeft.Y, bc.BottomRight.Y),
+ Top: MinInt(bc.TopLeft.Y, bc.TopRight.Y),
+ Left: MinInt(bc.TopLeft.X, bc.BottomLeft.X),
+ Right: MaxInt(bc.TopRight.X, bc.BottomRight.X),
+ Bottom: MaxInt(bc.BottomLeft.Y, bc.BottomRight.Y),
}
}
// Width returns the width
func (bc BoxCorners) Width() int {
- minLeft := util.Math.MinInt(bc.TopLeft.X, bc.BottomLeft.X)
- maxRight := util.Math.MaxInt(bc.TopRight.X, bc.BottomRight.X)
+ minLeft := MinInt(bc.TopLeft.X, bc.BottomLeft.X)
+ maxRight := MaxInt(bc.TopRight.X, bc.BottomRight.X)
return maxRight - minLeft
}
// Height returns the height
func (bc BoxCorners) Height() int {
- minTop := util.Math.MinInt(bc.TopLeft.Y, bc.TopRight.Y)
- maxBottom := util.Math.MaxInt(bc.BottomLeft.Y, bc.BottomRight.Y)
+ minTop := MinInt(bc.TopLeft.Y, bc.TopRight.Y)
+ maxBottom := MaxInt(bc.BottomLeft.Y, bc.BottomRight.Y)
return maxBottom - minTop
}
// Center returns the center of the box
func (bc BoxCorners) Center() (x, y int) {
- left := util.Math.MeanInt(bc.TopLeft.X, bc.BottomLeft.X)
- right := util.Math.MeanInt(bc.TopRight.X, bc.BottomRight.X)
+ left := MeanInt(bc.TopLeft.X, bc.BottomLeft.X)
+ right := MeanInt(bc.TopRight.X, bc.BottomRight.X)
x = ((right - left) >> 1) + left
- top := util.Math.MeanInt(bc.TopLeft.Y, bc.TopRight.Y)
- bottom := util.Math.MeanInt(bc.BottomLeft.Y, bc.BottomRight.Y)
+ top := MeanInt(bc.TopLeft.Y, bc.TopRight.Y)
+ bottom := MeanInt(bc.BottomLeft.Y, bc.BottomRight.Y)
y = ((bottom - top) >> 1) + top
return
@@ -303,12 +301,12 @@ func (bc BoxCorners) Center() (x, y int) {
func (bc BoxCorners) Rotate(thetaDegrees float64) BoxCorners {
cx, cy := bc.Center()
- thetaRadians := util.Math.DegreesToRadians(thetaDegrees)
+ thetaRadians := DegreesToRadians(thetaDegrees)
- tlx, tly := util.Math.RotateCoordinate(cx, cy, bc.TopLeft.X, bc.TopLeft.Y, thetaRadians)
- trx, try := util.Math.RotateCoordinate(cx, cy, bc.TopRight.X, bc.TopRight.Y, thetaRadians)
- brx, bry := util.Math.RotateCoordinate(cx, cy, bc.BottomRight.X, bc.BottomRight.Y, thetaRadians)
- blx, bly := util.Math.RotateCoordinate(cx, cy, bc.BottomLeft.X, bc.BottomLeft.Y, thetaRadians)
+ tlx, tly := RotateCoordinate(cx, cy, bc.TopLeft.X, bc.TopLeft.Y, thetaRadians)
+ trx, try := RotateCoordinate(cx, cy, bc.TopRight.X, bc.TopRight.Y, thetaRadians)
+ brx, bry := RotateCoordinate(cx, cy, bc.BottomRight.X, bc.BottomRight.Y, thetaRadians)
+ blx, bly := RotateCoordinate(cx, cy, bc.BottomLeft.X, bc.BottomLeft.Y, thetaRadians)
return BoxCorners{
TopLeft: Point{tlx, tly},
diff --git a/vendor/github.com/wcharczuk/go-chart/chart.go b/vendor/github.com/wcharczuk/go-chart/v2/chart.go
similarity index 89%
rename from vendor/github.com/wcharczuk/go-chart/chart.go
rename to vendor/github.com/wcharczuk/go-chart/v2/chart.go
index 83b70ec..5212a43 100644
--- a/vendor/github.com/wcharczuk/go-chart/chart.go
+++ b/vendor/github.com/wcharczuk/go-chart/v2/chart.go
@@ -7,7 +7,6 @@ import (
"math"
"github.com/golang/freetype/truetype"
- util "github.com/wcharczuk/go-chart/util"
)
// Chart is what we're drawing.
@@ -33,6 +32,8 @@ type Chart struct {
Series []Series
Elements []Renderable
+
+ Log Logger
}
// GetDPI returns the dpi for the chart.
@@ -75,8 +76,8 @@ func (c Chart) Render(rp RendererProvider, w io.Writer) error {
if len(c.Series) == 0 {
return errors.New("please provide at least one series")
}
- if visibleSeriesErr := c.checkHasVisibleSeries(); visibleSeriesErr != nil {
- return visibleSeriesErr
+ if err := c.checkHasVisibleSeries(); err != nil {
+ return err
}
c.YAxisSecondary.AxisType = YAxisSecondary
@@ -102,6 +103,8 @@ func (c Chart) Render(rp RendererProvider, w io.Writer) error {
canvasBox := c.getDefaultCanvasBox()
xf, yf, yfa := c.getValueFormatters()
+ Debugf(c.Log, "chart; canvas box: %v", canvasBox)
+
xr, yr, yra = c.setRangeDomains(canvasBox, xr, yr, yra)
err = c.checkRanges(xr, yr, yra)
@@ -115,6 +118,8 @@ func (c Chart) Render(rp RendererProvider, w io.Writer) error {
canvasBox = c.getAxesAdjustedCanvasBox(r, canvasBox, xr, yr, yra, xt, yt, yta)
xr, yr, yra = c.setRangeDomains(canvasBox, xr, yr, yra)
+ Debugf(c.Log, "chart; axes adjusted canvas box: %v", canvasBox)
+
// do a second pass in case things haven't settled yet.
xt, yt, yta = c.getAxesTicks(r, xr, yr, yra, xf, yf, yfa)
canvasBox = c.getAxesAdjustedCanvasBox(r, canvasBox, xr, yr, yra, xt, yt, yta)
@@ -125,6 +130,8 @@ func (c Chart) Render(rp RendererProvider, w io.Writer) error {
canvasBox = c.getAnnotationAdjustedCanvasBox(r, canvasBox, xr, yr, yra, xf, yf, yfa)
xr, yr, yra = c.setRangeDomains(canvasBox, xr, yr, yra)
xt, yt, yta = c.getAxesTicks(r, xr, yr, yra, xf, yf, yfa)
+
+ Debugf(c.Log, "chart; annotation adjusted canvas box: %v", canvasBox)
}
c.drawCanvas(r, canvasBox)
@@ -143,16 +150,14 @@ func (c Chart) Render(rp RendererProvider, w io.Writer) error {
}
func (c Chart) checkHasVisibleSeries() error {
- hasVisibleSeries := false
var style Style
for _, s := range c.Series {
style = s.GetStyle()
- hasVisibleSeries = hasVisibleSeries || (style.IsZero() || style.Show)
- }
- if !hasVisibleSeries {
- return fmt.Errorf("must have (1) visible series; make sure if you set a style, you set .Show = true")
+ if !style.Hidden {
+ return nil
+ }
}
- return nil
+ return fmt.Errorf("chart render; must have (1) visible series")
}
func (c Chart) validateSeries() error {
@@ -176,7 +181,7 @@ func (c Chart) getRanges() (xrange, yrange, yrangeAlt Range) {
// note: a possible future optimization is to not scan the series values if
// all axis are represented by either custom ticks or custom ranges.
for _, s := range c.Series {
- if s.GetStyle().IsZero() || s.GetStyle().Show {
+ if !s.GetStyle().Hidden {
seriesAxis := s.GetYAxis()
if bvp, isBoundedValuesProvider := s.(BoundedValuesProvider); isBoundedValuesProvider {
seriesLength := bvp.Len()
@@ -263,11 +268,10 @@ func (c Chart) getRanges() (xrange, yrange, yrangeAlt Range) {
yrange.SetMin(miny)
yrange.SetMax(maxy)
- // only round if we're showing the axis
- if c.YAxis.Style.Show {
+ if !c.YAxis.Style.Hidden {
delta := yrange.GetDelta()
- roundTo := util.Math.GetRoundToForDelta(delta)
- rmin, rmax := util.Math.RoundDown(yrange.GetMin(), roundTo), util.Math.RoundUp(yrange.GetMax(), roundTo)
+ roundTo := GetRoundToForDelta(delta)
+ rmin, rmax := RoundDown(yrange.GetMin(), roundTo), RoundUp(yrange.GetMax(), roundTo)
yrange.SetMin(rmin)
yrange.SetMax(rmax)
@@ -286,10 +290,10 @@ func (c Chart) getRanges() (xrange, yrange, yrangeAlt Range) {
yrangeAlt.SetMin(minya)
yrangeAlt.SetMax(maxya)
- if c.YAxisSecondary.Style.Show {
+ if !c.YAxisSecondary.Style.Hidden {
delta := yrangeAlt.GetDelta()
- roundTo := util.Math.GetRoundToForDelta(delta)
- rmin, rmax := util.Math.RoundDown(yrangeAlt.GetMin(), roundTo), util.Math.RoundUp(yrangeAlt.GetMax(), roundTo)
+ roundTo := GetRoundToForDelta(delta)
+ rmin, rmax := RoundDown(yrangeAlt.GetMin(), roundTo), RoundUp(yrangeAlt.GetMax(), roundTo)
yrangeAlt.SetMin(rmin)
yrangeAlt.SetMax(rmax)
}
@@ -299,6 +303,7 @@ func (c Chart) getRanges() (xrange, yrange, yrangeAlt Range) {
}
func (c Chart) checkRanges(xr, yr, yra Range) error {
+ Debugf(c.Log, "checking xrange: %v", xr)
xDelta := xr.GetDelta()
if math.IsInf(xDelta, 0) {
return errors.New("infinite x-range delta")
@@ -310,6 +315,7 @@ func (c Chart) checkRanges(xr, yr, yra Range) error {
return errors.New("zero x-range delta; there needs to be at least (2) values")
}
+ Debugf(c.Log, "checking yrange: %v", yr)
yDelta := yr.GetDelta()
if math.IsInf(yDelta, 0) {
return errors.New("infinite y-range delta")
@@ -317,11 +323,9 @@ func (c Chart) checkRanges(xr, yr, yra Range) error {
if math.IsNaN(yDelta) {
return errors.New("nan y-range delta")
}
- if yDelta == 0 {
- return errors.New("zero y-range delta")
- }
if c.hasSecondarySeries() {
+ Debugf(c.Log, "checking secondary yrange: %v", yra)
yraDelta := yra.GetDelta()
if math.IsInf(yraDelta, 0) {
return errors.New("infinite secondary y-range delta")
@@ -329,9 +333,6 @@ func (c Chart) checkRanges(xr, yr, yra Range) error {
if math.IsNaN(yraDelta) {
return errors.New("nan secondary y-range delta")
}
- if yraDelta == 0 {
- return errors.New("zero secondary y-range delta")
- }
}
return nil
@@ -367,17 +368,17 @@ func (c Chart) getValueFormatters() (x, y, ya ValueFormatter) {
}
func (c Chart) hasAxes() bool {
- return c.XAxis.Style.Show || c.YAxis.Style.Show || c.YAxisSecondary.Style.Show
+ return !c.XAxis.Style.Hidden || !c.YAxis.Style.Hidden || !c.YAxisSecondary.Style.Hidden
}
func (c Chart) getAxesTicks(r Renderer, xr, yr, yar Range, xf, yf, yfa ValueFormatter) (xticks, yticks, yticksAlt []Tick) {
- if c.XAxis.Style.Show {
+ if !c.XAxis.Style.Hidden {
xticks = c.XAxis.GetTicks(r, xr, c.styleDefaultsAxes(), xf)
}
- if c.YAxis.Style.Show {
+ if !c.YAxis.Style.Hidden {
yticks = c.YAxis.GetTicks(r, yr, c.styleDefaultsAxes(), yf)
}
- if c.YAxisSecondary.Style.Show {
+ if !c.YAxisSecondary.Style.Hidden {
yticksAlt = c.YAxisSecondary.GetTicks(r, yar, c.styleDefaultsAxes(), yfa)
}
return
@@ -385,16 +386,19 @@ func (c Chart) getAxesTicks(r Renderer, xr, yr, yar Range, xf, yf, yfa ValueForm
func (c Chart) getAxesAdjustedCanvasBox(r Renderer, canvasBox Box, xr, yr, yra Range, xticks, yticks, yticksAlt []Tick) Box {
axesOuterBox := canvasBox.Clone()
- if c.XAxis.Style.Show {
+ if !c.XAxis.Style.Hidden {
axesBounds := c.XAxis.Measure(r, canvasBox, xr, c.styleDefaultsAxes(), xticks)
+ Debugf(c.Log, "chart; x-axis measured %v", axesBounds)
axesOuterBox = axesOuterBox.Grow(axesBounds)
}
- if c.YAxis.Style.Show {
+ if !c.YAxis.Style.Hidden {
axesBounds := c.YAxis.Measure(r, canvasBox, yr, c.styleDefaultsAxes(), yticks)
+ Debugf(c.Log, "chart; y-axis measured %v", axesBounds)
axesOuterBox = axesOuterBox.Grow(axesBounds)
}
- if c.YAxisSecondary.Style.Show {
+ if !c.YAxisSecondary.Style.Hidden && c.hasSecondarySeries() {
axesBounds := c.YAxisSecondary.Measure(r, canvasBox, yra, c.styleDefaultsAxes(), yticksAlt)
+ Debugf(c.Log, "chart; y-axis secondary measured %v", axesBounds)
axesOuterBox = axesOuterBox.Grow(axesBounds)
}
@@ -411,7 +415,7 @@ func (c Chart) setRangeDomains(canvasBox Box, xr, yr, yra Range) (Range, Range,
func (c Chart) hasAnnotationSeries() bool {
for _, s := range c.Series {
if as, isAnnotationSeries := s.(AnnotationSeries); isAnnotationSeries {
- if as.Style.IsZero() || as.Style.Show {
+ if !as.GetStyle().Hidden {
return true
}
}
@@ -432,7 +436,7 @@ func (c Chart) getAnnotationAdjustedCanvasBox(r Renderer, canvasBox Box, xr, yr,
annotationSeriesBox := canvasBox.Clone()
for seriesIndex, s := range c.Series {
if as, isAnnotationSeries := s.(AnnotationSeries); isAnnotationSeries {
- if as.Style.IsZero() || as.Style.Show {
+ if !as.GetStyle().Hidden {
style := c.styleDefaultsSeries(seriesIndex)
var annotationBounds Box
if as.YAxis == YAxisPrimary {
@@ -469,19 +473,19 @@ func (c Chart) drawCanvas(r Renderer, canvasBox Box) {
}
func (c Chart) drawAxes(r Renderer, canvasBox Box, xrange, yrange, yrangeAlt Range, xticks, yticks, yticksAlt []Tick) {
- if c.XAxis.Style.Show {
+ if !c.XAxis.Style.Hidden {
c.XAxis.Render(r, canvasBox, xrange, c.styleDefaultsAxes(), xticks)
}
- if c.YAxis.Style.Show {
+ if !c.YAxis.Style.Hidden {
c.YAxis.Render(r, canvasBox, yrange, c.styleDefaultsAxes(), yticks)
}
- if c.YAxisSecondary.Style.Show {
+ if !c.YAxisSecondary.Style.Hidden {
c.YAxisSecondary.Render(r, canvasBox, yrangeAlt, c.styleDefaultsAxes(), yticksAlt)
}
}
func (c Chart) drawSeries(r Renderer, canvasBox Box, xrange, yrange, yrangeAlt Range, s Series, seriesIndex int) {
- if s.GetStyle().IsZero() || s.GetStyle().Show {
+ if !s.GetStyle().Hidden {
if s.GetYAxis() == YAxisPrimary {
s.Render(r, canvasBox, xrange, yrange, c.styleDefaultsSeries(seriesIndex))
} else if s.GetYAxis() == YAxisSecondary {
@@ -491,7 +495,7 @@ func (c Chart) drawSeries(r Renderer, canvasBox Box, xrange, yrange, yrangeAlt R
}
func (c Chart) drawTitle(r Renderer) {
- if len(c.Title) > 0 && c.TitleStyle.Show {
+ if len(c.Title) > 0 && !c.TitleStyle.Hidden {
r.SetFont(c.TitleStyle.GetFont(c.GetFont()))
r.SetFontColor(c.TitleStyle.GetFontColor(c.GetColorPalette().TextColor()))
titleFontSize := c.TitleStyle.GetFontSize(DefaultTitleFontSize)
diff --git a/vendor/github.com/wcharczuk/go-chart/colors.go b/vendor/github.com/wcharczuk/go-chart/v2/colors.go
similarity index 99%
rename from vendor/github.com/wcharczuk/go-chart/colors.go
rename to vendor/github.com/wcharczuk/go-chart/v2/colors.go
index 87dd4f0..b51f9ea 100644
--- a/vendor/github.com/wcharczuk/go-chart/colors.go
+++ b/vendor/github.com/wcharczuk/go-chart/v2/colors.go
@@ -1,6 +1,6 @@
package chart
-import "github.com/wcharczuk/go-chart/drawing"
+import "github.com/wcharczuk/go-chart/v2/drawing"
var (
// ColorWhite is white.
diff --git a/vendor/github.com/wcharczuk/go-chart/concat_series.go b/vendor/github.com/wcharczuk/go-chart/v2/concat_series.go
similarity index 100%
rename from vendor/github.com/wcharczuk/go-chart/concat_series.go
rename to vendor/github.com/wcharczuk/go-chart/v2/concat_series.go
diff --git a/vendor/github.com/wcharczuk/go-chart/continuous_range.go b/vendor/github.com/wcharczuk/go-chart/v2/continuous_range.go
similarity index 96%
rename from vendor/github.com/wcharczuk/go-chart/continuous_range.go
rename to vendor/github.com/wcharczuk/go-chart/v2/continuous_range.go
index 99fa939..517b727 100644
--- a/vendor/github.com/wcharczuk/go-chart/continuous_range.go
+++ b/vendor/github.com/wcharczuk/go-chart/v2/continuous_range.go
@@ -62,6 +62,9 @@ func (r *ContinuousRange) SetDomain(domain int) {
// String returns a simple string for the ContinuousRange.
func (r ContinuousRange) String() string {
+ if r.GetDelta() == 0 {
+ return "ContinuousRange [empty]"
+ }
return fmt.Sprintf("ContinuousRange [%.2f,%.2f] => %d", r.Min, r.Max, r.Domain)
}
diff --git a/vendor/github.com/wcharczuk/go-chart/continuous_series.go b/vendor/github.com/wcharczuk/go-chart/v2/continuous_series.go
similarity index 75%
rename from vendor/github.com/wcharczuk/go-chart/continuous_series.go
rename to vendor/github.com/wcharczuk/go-chart/v2/continuous_series.go
index bca80de..73c7ab7 100644
--- a/vendor/github.com/wcharczuk/go-chart/continuous_series.go
+++ b/vendor/github.com/wcharczuk/go-chart/v2/continuous_series.go
@@ -2,6 +2,13 @@ package chart
import "fmt"
+// Interface Assertions.
+var (
+ _ Series = (*ContinuousSeries)(nil)
+ _ FirstValuesProvider = (*ContinuousSeries)(nil)
+ _ LastValuesProvider = (*ContinuousSeries)(nil)
+)
+
// ContinuousSeries represents a line on a chart.
type ContinuousSeries struct {
Name string
@@ -36,6 +43,11 @@ func (cs ContinuousSeries) GetValues(index int) (float64, float64) {
return cs.XValues[index], cs.YValues[index]
}
+// GetFirstValues gets the first x,y values.
+func (cs ContinuousSeries) GetFirstValues() (float64, float64) {
+ return cs.XValues[0], cs.YValues[0]
+}
+
// GetLastValues gets the last x,y values.
func (cs ContinuousSeries) GetLastValues() (float64, float64) {
return cs.XValues[len(cs.XValues)-1], cs.YValues[len(cs.YValues)-1]
@@ -70,11 +82,15 @@ func (cs ContinuousSeries) Render(r Renderer, canvasBox Box, xrange, yrange Rang
// Validate validates the series.
func (cs ContinuousSeries) Validate() error {
if len(cs.XValues) == 0 {
- return fmt.Errorf("continuous series must have xvalues set")
+ return fmt.Errorf("continuous series; must have xvalues set")
}
if len(cs.YValues) == 0 {
- return fmt.Errorf("continuous series must have yvalues set")
+ return fmt.Errorf("continuous series; must have yvalues set")
+ }
+
+ if len(cs.XValues) != len(cs.YValues) {
+ return fmt.Errorf("continuous series; must have same length xvalues as yvalues")
}
return nil
}
diff --git a/vendor/github.com/wcharczuk/go-chart/defaults.go b/vendor/github.com/wcharczuk/go-chart/v2/defaults.go
similarity index 100%
rename from vendor/github.com/wcharczuk/go-chart/defaults.go
rename to vendor/github.com/wcharczuk/go-chart/v2/defaults.go
diff --git a/vendor/github.com/wcharczuk/go-chart/v2/donut_chart.go b/vendor/github.com/wcharczuk/go-chart/v2/donut_chart.go
new file mode 100644
index 0000000..f5a7854
--- /dev/null
+++ b/vendor/github.com/wcharczuk/go-chart/v2/donut_chart.go
@@ -0,0 +1,315 @@
+package chart
+
+import (
+ "errors"
+ "fmt"
+ "io"
+
+ "github.com/golang/freetype/truetype"
+)
+
+// DonutChart is a chart that draws sections of a circle based on percentages with an hole.
+type DonutChart struct {
+ Title string
+ TitleStyle Style
+
+ ColorPalette ColorPalette
+
+ Width int
+ Height int
+ DPI float64
+
+ Background Style
+ Canvas Style
+ SliceStyle Style
+
+ Font *truetype.Font
+ defaultFont *truetype.Font
+
+ Values []Value
+ Elements []Renderable
+}
+
+// GetDPI returns the dpi for the chart.
+func (pc DonutChart) GetDPI(defaults ...float64) float64 {
+ if pc.DPI == 0 {
+ if len(defaults) > 0 {
+ return defaults[0]
+ }
+ return DefaultDPI
+ }
+ return pc.DPI
+}
+
+// GetFont returns the text font.
+func (pc DonutChart) GetFont() *truetype.Font {
+ if pc.Font == nil {
+ return pc.defaultFont
+ }
+ return pc.Font
+}
+
+// GetWidth returns the chart width or the default value.
+func (pc DonutChart) GetWidth() int {
+ if pc.Width == 0 {
+ return DefaultChartWidth
+ }
+ return pc.Width
+}
+
+// GetHeight returns the chart height or the default value.
+func (pc DonutChart) GetHeight() int {
+ if pc.Height == 0 {
+ return DefaultChartWidth
+ }
+ return pc.Height
+}
+
+// Render renders the chart with the given renderer to the given io.Writer.
+func (pc DonutChart) Render(rp RendererProvider, w io.Writer) error {
+ if len(pc.Values) == 0 {
+ return errors.New("please provide at least one value")
+ }
+
+ r, err := rp(pc.GetWidth(), pc.GetHeight())
+ if err != nil {
+ return err
+ }
+
+ if pc.Font == nil {
+ defaultFont, err := GetDefaultFont()
+ if err != nil {
+ return err
+ }
+ pc.defaultFont = defaultFont
+ }
+ r.SetDPI(pc.GetDPI(DefaultDPI))
+
+ canvasBox := pc.getDefaultCanvasBox()
+ canvasBox = pc.getCircleAdjustedCanvasBox(canvasBox)
+
+ pc.drawBackground(r)
+ pc.drawCanvas(r, canvasBox)
+
+ finalValues, err := pc.finalizeValues(pc.Values)
+ if err != nil {
+ return err
+ }
+ pc.drawSlices(r, canvasBox, finalValues)
+ pc.drawTitle(r)
+ for _, a := range pc.Elements {
+ a(r, canvasBox, pc.styleDefaultsElements())
+ }
+
+ return r.Save(w)
+}
+
+func (pc DonutChart) drawBackground(r Renderer) {
+ Draw.Box(r, Box{
+ Right: pc.GetWidth(),
+ Bottom: pc.GetHeight(),
+ }, pc.getBackgroundStyle())
+}
+
+func (pc DonutChart) drawCanvas(r Renderer, canvasBox Box) {
+ Draw.Box(r, canvasBox, pc.getCanvasStyle())
+}
+
+func (pc DonutChart) drawTitle(r Renderer) {
+ if len(pc.Title) > 0 && !pc.TitleStyle.Hidden {
+ Draw.TextWithin(r, pc.Title, pc.Box(), pc.styleDefaultsTitle())
+ }
+}
+
+func (pc DonutChart) drawSlices(r Renderer, canvasBox Box, values []Value) {
+ cx, cy := canvasBox.Center()
+ diameter := MinInt(canvasBox.Width(), canvasBox.Height())
+ radius := float64(diameter>>1) / 1.1
+ labelRadius := (radius * 2.83) / 3.0
+
+ // draw the donut slices
+ var rads, delta, delta2, total float64
+ var lx, ly int
+
+ if len(values) == 1 {
+ pc.styleDonutChartValue(0).WriteToRenderer(r)
+ r.MoveTo(cx, cy)
+ r.Circle(radius, cx, cy)
+ } else {
+ for index, v := range values {
+ v.Style.InheritFrom(pc.styleDonutChartValue(index)).WriteToRenderer(r)
+ r.MoveTo(cx, cy)
+ rads = PercentToRadians(total)
+ delta = PercentToRadians(v.Value)
+
+ r.ArcTo(cx, cy, (radius / 1.25), (radius / 1.25), rads, delta)
+
+ r.LineTo(cx, cy)
+ r.Close()
+ r.FillStroke()
+ total = total + v.Value
+ }
+ }
+
+ //making the donut hole
+ v := Value{Value: 100, Label: "center"}
+ styletemp := pc.SliceStyle.InheritFrom(Style{
+ StrokeColor: ColorWhite, StrokeWidth: 4.0, FillColor: ColorWhite, FontColor: ColorWhite, //Font: pc.GetFont(),//FontSize: pc.getScaledFontSize(),
+ })
+ v.Style.InheritFrom(styletemp).WriteToRenderer(r)
+ r.MoveTo(cx, cy)
+ r.ArcTo(cx, cy, (radius / 3.5), (radius / 3.5), DegreesToRadians(0), DegreesToRadians(359))
+ r.LineTo(cx, cy)
+ r.Close()
+ r.FillStroke()
+
+ // draw the labels
+ total = 0
+ for index, v := range values {
+ v.Style.InheritFrom(pc.styleDonutChartValue(index)).WriteToRenderer(r)
+ if len(v.Label) > 0 {
+ delta2 = PercentToRadians(total + (v.Value / 2.0))
+ delta2 = RadianAdd(delta2, _pi2)
+ lx, ly = CirclePoint(cx, cy, labelRadius, delta2)
+
+ tb := r.MeasureText(v.Label)
+ lx = lx - (tb.Width() >> 1)
+ ly = ly + (tb.Height() >> 1)
+
+ r.Text(v.Label, lx, ly)
+ }
+ total = total + v.Value
+ }
+}
+
+func (pc DonutChart) finalizeValues(values []Value) ([]Value, error) {
+ finalValues := Values(values).Normalize()
+ if len(finalValues) == 0 {
+ return nil, fmt.Errorf("donut chart must contain at least (1) non-zero value")
+ }
+ return finalValues, nil
+}
+
+func (pc DonutChart) getDefaultCanvasBox() Box {
+ return pc.Box()
+}
+
+func (pc DonutChart) getCircleAdjustedCanvasBox(canvasBox Box) Box {
+ circleDiameter := MinInt(canvasBox.Width(), canvasBox.Height())
+
+ square := Box{
+ Right: circleDiameter,
+ Bottom: circleDiameter,
+ }
+
+ return canvasBox.Fit(square)
+}
+
+func (pc DonutChart) getBackgroundStyle() Style {
+ return pc.Background.InheritFrom(pc.styleDefaultsBackground())
+}
+
+func (pc DonutChart) getCanvasStyle() Style {
+ return pc.Canvas.InheritFrom(pc.styleDefaultsCanvas())
+}
+
+func (pc DonutChart) styleDefaultsCanvas() Style {
+ return Style{
+ FillColor: pc.GetColorPalette().CanvasColor(),
+ StrokeColor: pc.GetColorPalette().CanvasStrokeColor(),
+ StrokeWidth: DefaultStrokeWidth,
+ }
+}
+
+func (pc DonutChart) styleDefaultsDonutChartValue() Style {
+ return Style{
+ StrokeColor: pc.GetColorPalette().TextColor(),
+ StrokeWidth: 4.0,
+ FillColor: pc.GetColorPalette().TextColor(),
+ }
+}
+
+func (pc DonutChart) styleDonutChartValue(index int) Style {
+ return pc.SliceStyle.InheritFrom(Style{
+ StrokeColor: ColorWhite,
+ StrokeWidth: 4.0,
+ FillColor: pc.GetColorPalette().GetSeriesColor(index),
+ FontSize: pc.getScaledFontSize(),
+ FontColor: pc.GetColorPalette().TextColor(),
+ Font: pc.GetFont(),
+ })
+}
+
+func (pc DonutChart) getScaledFontSize() float64 {
+ effectiveDimension := MinInt(pc.GetWidth(), pc.GetHeight())
+ if effectiveDimension >= 2048 {
+ return 48.0
+ } else if effectiveDimension >= 1024 {
+ return 24.0
+ } else if effectiveDimension > 512 {
+ return 18.0
+ } else if effectiveDimension > 256 {
+ return 12.0
+ }
+ return 10.0
+}
+
+func (pc DonutChart) styleDefaultsBackground() Style {
+ return Style{
+ FillColor: pc.GetColorPalette().BackgroundColor(),
+ StrokeColor: pc.GetColorPalette().BackgroundStrokeColor(),
+ StrokeWidth: DefaultStrokeWidth,
+ }
+}
+
+func (pc DonutChart) styleDefaultsElements() Style {
+ return Style{
+ Font: pc.GetFont(),
+ }
+}
+
+func (pc DonutChart) styleDefaultsTitle() Style {
+ return pc.TitleStyle.InheritFrom(Style{
+ FontColor: pc.GetColorPalette().TextColor(),
+ Font: pc.GetFont(),
+ FontSize: pc.getTitleFontSize(),
+ TextHorizontalAlign: TextHorizontalAlignCenter,
+ TextVerticalAlign: TextVerticalAlignTop,
+ TextWrap: TextWrapWord,
+ })
+}
+
+func (pc DonutChart) getTitleFontSize() float64 {
+ effectiveDimension := MinInt(pc.GetWidth(), pc.GetHeight())
+ if effectiveDimension >= 2048 {
+ return 48
+ } else if effectiveDimension >= 1024 {
+ return 24
+ } else if effectiveDimension >= 512 {
+ return 18
+ } else if effectiveDimension >= 256 {
+ return 12
+ }
+ return 10
+}
+
+// GetColorPalette returns the color palette for the chart.
+func (pc DonutChart) GetColorPalette() ColorPalette {
+ if pc.ColorPalette != nil {
+ return pc.ColorPalette
+ }
+ return AlternateColorPalette
+}
+
+// Box returns the chart bounds as a box.
+func (pc DonutChart) Box() Box {
+ dpr := pc.Background.Padding.GetRight(DefaultBackgroundPadding.Right)
+ dpb := pc.Background.Padding.GetBottom(DefaultBackgroundPadding.Bottom)
+
+ return Box{
+ Top: pc.Background.Padding.GetTop(DefaultBackgroundPadding.Top),
+ Left: pc.Background.Padding.GetLeft(DefaultBackgroundPadding.Left),
+ Right: pc.GetWidth() - dpr,
+ Bottom: pc.GetHeight() - dpb,
+ }
+}
diff --git a/vendor/github.com/wcharczuk/go-chart/draw.go b/vendor/github.com/wcharczuk/go-chart/v2/draw.go
similarity index 97%
rename from vendor/github.com/wcharczuk/go-chart/draw.go
rename to vendor/github.com/wcharczuk/go-chart/v2/draw.go
index ef79dc6..e188079 100644
--- a/vendor/github.com/wcharczuk/go-chart/draw.go
+++ b/vendor/github.com/wcharczuk/go-chart/v2/draw.go
@@ -2,8 +2,6 @@ package chart
import (
"math"
-
- util "github.com/wcharczuk/go-chart/util"
)
var (
@@ -40,8 +38,8 @@ func (d draw) LineSeries(r Renderer, canvasBox Box, xrange, yrange Range, style
y = cb - yrange.Translate(vy)
r.LineTo(x, y)
}
- r.LineTo(x, util.Math.MinInt(cb, cb-yv0))
- r.LineTo(x0, util.Math.MinInt(cb, cb-yv0))
+ r.LineTo(x, MinInt(cb, cb-yv0))
+ r.LineTo(x0, MinInt(cb, cb-yv0))
r.LineTo(x0, y0)
r.Fill()
}
@@ -298,8 +296,10 @@ func (d draw) TextWithin(r Renderer, text string, box Box, style Style) {
switch style.GetTextVerticalAlign() {
case TextVerticalAlignBottom, TextVerticalAlignBaseline: // i have to build better baseline handling into measure text
y = y - linesBox.Height()
- case TextVerticalAlignMiddle, TextVerticalAlignMiddleBaseline:
- y = (y - linesBox.Height()) >> 1
+ case TextVerticalAlignMiddle:
+ y = y + (box.Height() >> 1) - (linesBox.Height() >> 1)
+ case TextVerticalAlignMiddleBaseline:
+ y = y + (box.Height() >> 1) - linesBox.Height()
}
var tx, ty int
diff --git a/vendor/github.com/wcharczuk/go-chart/drawing/README.md b/vendor/github.com/wcharczuk/go-chart/v2/drawing/README.md
similarity index 100%
rename from vendor/github.com/wcharczuk/go-chart/drawing/README.md
rename to vendor/github.com/wcharczuk/go-chart/v2/drawing/README.md
diff --git a/vendor/github.com/wcharczuk/go-chart/drawing/color.go b/vendor/github.com/wcharczuk/go-chart/v2/drawing/color.go
similarity index 100%
rename from vendor/github.com/wcharczuk/go-chart/drawing/color.go
rename to vendor/github.com/wcharczuk/go-chart/v2/drawing/color.go
diff --git a/vendor/github.com/wcharczuk/go-chart/drawing/constants.go b/vendor/github.com/wcharczuk/go-chart/v2/drawing/constants.go
similarity index 100%
rename from vendor/github.com/wcharczuk/go-chart/drawing/constants.go
rename to vendor/github.com/wcharczuk/go-chart/v2/drawing/constants.go
diff --git a/vendor/github.com/wcharczuk/go-chart/drawing/curve.go b/vendor/github.com/wcharczuk/go-chart/v2/drawing/curve.go
similarity index 100%
rename from vendor/github.com/wcharczuk/go-chart/drawing/curve.go
rename to vendor/github.com/wcharczuk/go-chart/v2/drawing/curve.go
diff --git a/vendor/github.com/wcharczuk/go-chart/drawing/dasher.go b/vendor/github.com/wcharczuk/go-chart/v2/drawing/dasher.go
similarity index 100%
rename from vendor/github.com/wcharczuk/go-chart/drawing/dasher.go
rename to vendor/github.com/wcharczuk/go-chart/v2/drawing/dasher.go
diff --git a/vendor/github.com/wcharczuk/go-chart/drawing/demux_flattener.go b/vendor/github.com/wcharczuk/go-chart/v2/drawing/demux_flattener.go
similarity index 100%
rename from vendor/github.com/wcharczuk/go-chart/drawing/demux_flattener.go
rename to vendor/github.com/wcharczuk/go-chart/v2/drawing/demux_flattener.go
diff --git a/vendor/github.com/wcharczuk/go-chart/drawing/drawing.go b/vendor/github.com/wcharczuk/go-chart/v2/drawing/drawing.go
similarity index 100%
rename from vendor/github.com/wcharczuk/go-chart/drawing/drawing.go
rename to vendor/github.com/wcharczuk/go-chart/v2/drawing/drawing.go
diff --git a/vendor/github.com/wcharczuk/go-chart/drawing/flattener.go b/vendor/github.com/wcharczuk/go-chart/v2/drawing/flattener.go
similarity index 100%
rename from vendor/github.com/wcharczuk/go-chart/drawing/flattener.go
rename to vendor/github.com/wcharczuk/go-chart/v2/drawing/flattener.go
diff --git a/vendor/github.com/wcharczuk/go-chart/drawing/free_type_path.go b/vendor/github.com/wcharczuk/go-chart/v2/drawing/free_type_path.go
similarity index 100%
rename from vendor/github.com/wcharczuk/go-chart/drawing/free_type_path.go
rename to vendor/github.com/wcharczuk/go-chart/v2/drawing/free_type_path.go
diff --git a/vendor/github.com/wcharczuk/go-chart/drawing/graphic_context.go b/vendor/github.com/wcharczuk/go-chart/v2/drawing/graphic_context.go
similarity index 100%
rename from vendor/github.com/wcharczuk/go-chart/drawing/graphic_context.go
rename to vendor/github.com/wcharczuk/go-chart/v2/drawing/graphic_context.go
diff --git a/vendor/github.com/wcharczuk/go-chart/drawing/image_filter.go b/vendor/github.com/wcharczuk/go-chart/v2/drawing/image_filter.go
similarity index 100%
rename from vendor/github.com/wcharczuk/go-chart/drawing/image_filter.go
rename to vendor/github.com/wcharczuk/go-chart/v2/drawing/image_filter.go
diff --git a/vendor/github.com/wcharczuk/go-chart/drawing/line.go b/vendor/github.com/wcharczuk/go-chart/v2/drawing/line.go
similarity index 100%
rename from vendor/github.com/wcharczuk/go-chart/drawing/line.go
rename to vendor/github.com/wcharczuk/go-chart/v2/drawing/line.go
diff --git a/vendor/github.com/wcharczuk/go-chart/drawing/matrix.go b/vendor/github.com/wcharczuk/go-chart/v2/drawing/matrix.go
similarity index 100%
rename from vendor/github.com/wcharczuk/go-chart/drawing/matrix.go
rename to vendor/github.com/wcharczuk/go-chart/v2/drawing/matrix.go
diff --git a/vendor/github.com/wcharczuk/go-chart/drawing/painter.go b/vendor/github.com/wcharczuk/go-chart/v2/drawing/painter.go
similarity index 100%
rename from vendor/github.com/wcharczuk/go-chart/drawing/painter.go
rename to vendor/github.com/wcharczuk/go-chart/v2/drawing/painter.go
diff --git a/vendor/github.com/wcharczuk/go-chart/drawing/path.go b/vendor/github.com/wcharczuk/go-chart/v2/drawing/path.go
similarity index 100%
rename from vendor/github.com/wcharczuk/go-chart/drawing/path.go
rename to vendor/github.com/wcharczuk/go-chart/v2/drawing/path.go
diff --git a/vendor/github.com/wcharczuk/go-chart/drawing/raster_graphic_context.go b/vendor/github.com/wcharczuk/go-chart/v2/drawing/raster_graphic_context.go
similarity index 100%
rename from vendor/github.com/wcharczuk/go-chart/drawing/raster_graphic_context.go
rename to vendor/github.com/wcharczuk/go-chart/v2/drawing/raster_graphic_context.go
diff --git a/vendor/github.com/wcharczuk/go-chart/drawing/stack_graphic_context.go b/vendor/github.com/wcharczuk/go-chart/v2/drawing/stack_graphic_context.go
similarity index 100%
rename from vendor/github.com/wcharczuk/go-chart/drawing/stack_graphic_context.go
rename to vendor/github.com/wcharczuk/go-chart/v2/drawing/stack_graphic_context.go
diff --git a/vendor/github.com/wcharczuk/go-chart/drawing/stroker.go b/vendor/github.com/wcharczuk/go-chart/v2/drawing/stroker.go
similarity index 100%
rename from vendor/github.com/wcharczuk/go-chart/drawing/stroker.go
rename to vendor/github.com/wcharczuk/go-chart/v2/drawing/stroker.go
diff --git a/vendor/github.com/wcharczuk/go-chart/drawing/text.go b/vendor/github.com/wcharczuk/go-chart/v2/drawing/text.go
similarity index 100%
rename from vendor/github.com/wcharczuk/go-chart/drawing/text.go
rename to vendor/github.com/wcharczuk/go-chart/v2/drawing/text.go
diff --git a/vendor/github.com/wcharczuk/go-chart/drawing/transformer.go b/vendor/github.com/wcharczuk/go-chart/v2/drawing/transformer.go
similarity index 100%
rename from vendor/github.com/wcharczuk/go-chart/drawing/transformer.go
rename to vendor/github.com/wcharczuk/go-chart/v2/drawing/transformer.go
diff --git a/vendor/github.com/wcharczuk/go-chart/drawing/util.go b/vendor/github.com/wcharczuk/go-chart/v2/drawing/util.go
similarity index 100%
rename from vendor/github.com/wcharczuk/go-chart/drawing/util.go
rename to vendor/github.com/wcharczuk/go-chart/v2/drawing/util.go
diff --git a/vendor/github.com/wcharczuk/go-chart/ema_series.go b/vendor/github.com/wcharczuk/go-chart/v2/ema_series.go
similarity index 85%
rename from vendor/github.com/wcharczuk/go-chart/ema_series.go
rename to vendor/github.com/wcharczuk/go-chart/v2/ema_series.go
index ceaec39..44415b5 100644
--- a/vendor/github.com/wcharczuk/go-chart/ema_series.go
+++ b/vendor/github.com/wcharczuk/go-chart/v2/ema_series.go
@@ -7,6 +7,13 @@ const (
DefaultEMAPeriod = 12
)
+// Interface Assertions.
+var (
+ _ Series = (*EMASeries)(nil)
+ _ FirstValuesProvider = (*EMASeries)(nil)
+ _ LastValuesProvider = (*EMASeries)(nil)
+)
+
// EMASeries is a computed series.
type EMASeries struct {
Name string
@@ -66,6 +73,19 @@ func (ema *EMASeries) GetValues(index int) (x, y float64) {
return
}
+// GetFirstValues computes the first moving average value.
+func (ema *EMASeries) GetFirstValues() (x, y float64) {
+ if ema.InnerSeries == nil {
+ return
+ }
+ if len(ema.cache) == 0 {
+ ema.ensureCachedValues()
+ }
+ x, _ = ema.InnerSeries.GetValues(0)
+ y = ema.cache[0]
+ return
+}
+
// GetLastValues computes the last moving average value but walking back window size samples,
// and recomputing the last moving average chunk.
func (ema *EMASeries) GetLastValues() (x, y float64) {
diff --git a/vendor/github.com/wcharczuk/go-chart/v2/fileutil.go b/vendor/github.com/wcharczuk/go-chart/v2/fileutil.go
new file mode 100644
index 0000000..f1a7768
--- /dev/null
+++ b/vendor/github.com/wcharczuk/go-chart/v2/fileutil.go
@@ -0,0 +1,49 @@
+package chart
+
+import (
+ "bufio"
+ "io"
+ "os"
+)
+
+// ReadLines reads a file and calls the handler for each line.
+func ReadLines(filePath string, handler func(string) error) error {
+ f, err := os.Open(filePath)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+
+ scanner := bufio.NewScanner(f)
+ for scanner.Scan() {
+ line := scanner.Text()
+ err = handler(line)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// ReadChunks reads a file in `chunkSize` pieces, dispatched to the handler.
+func ReadChunks(filePath string, chunkSize int, handler func([]byte) error) error {
+ f, err := os.Open(filePath)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+
+ chunk := make([]byte, chunkSize)
+ for {
+ readBytes, err := f.Read(chunk)
+ if err == io.EOF {
+ break
+ }
+ readData := chunk[:readBytes]
+ err = handler(readData)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
diff --git a/vendor/github.com/wcharczuk/go-chart/v2/first_value_annotation.go b/vendor/github.com/wcharczuk/go-chart/v2/first_value_annotation.go
new file mode 100644
index 0000000..2b214b3
--- /dev/null
+++ b/vendor/github.com/wcharczuk/go-chart/v2/first_value_annotation.go
@@ -0,0 +1,37 @@
+package chart
+
+import "fmt"
+
+// FirstValueAnnotation returns an annotation series of just the first value of a value provider as an annotation.
+func FirstValueAnnotation(innerSeries ValuesProvider, vfs ...ValueFormatter) AnnotationSeries {
+ var vf ValueFormatter
+ if len(vfs) > 0 {
+ vf = vfs[0]
+ } else if typed, isTyped := innerSeries.(ValueFormatterProvider); isTyped {
+ _, vf = typed.GetValueFormatters()
+ } else {
+ vf = FloatValueFormatter
+ }
+
+ var firstValue Value2
+ if typed, isTyped := innerSeries.(FirstValuesProvider); isTyped {
+ firstValue.XValue, firstValue.YValue = typed.GetFirstValues()
+ firstValue.Label = vf(firstValue.YValue)
+ } else {
+ firstValue.XValue, firstValue.YValue = innerSeries.GetValues(0)
+ firstValue.Label = vf(firstValue.YValue)
+ }
+
+ var seriesName string
+ var seriesStyle Style
+ if typed, isTyped := innerSeries.(Series); isTyped {
+ seriesName = fmt.Sprintf("%s - First Value", typed.GetName())
+ seriesStyle = typed.GetStyle()
+ }
+
+ return AnnotationSeries{
+ Name: seriesName,
+ Style: seriesStyle,
+ Annotations: []Value2{firstValue},
+ }
+}
diff --git a/vendor/github.com/wcharczuk/go-chart/font.go b/vendor/github.com/wcharczuk/go-chart/v2/font.go
similarity index 92%
rename from vendor/github.com/wcharczuk/go-chart/font.go
rename to vendor/github.com/wcharczuk/go-chart/v2/font.go
index a82880c..401143b 100644
--- a/vendor/github.com/wcharczuk/go-chart/font.go
+++ b/vendor/github.com/wcharczuk/go-chart/v2/font.go
@@ -4,7 +4,7 @@ import (
"sync"
"github.com/golang/freetype/truetype"
- "github.com/wcharczuk/go-chart/roboto"
+ "github.com/wcharczuk/go-chart/v2/roboto"
)
var (
diff --git a/vendor/github.com/wcharczuk/go-chart/grid_line.go b/vendor/github.com/wcharczuk/go-chart/v2/grid_line.go
similarity index 100%
rename from vendor/github.com/wcharczuk/go-chart/grid_line.go
rename to vendor/github.com/wcharczuk/go-chart/v2/grid_line.go
diff --git a/vendor/github.com/wcharczuk/go-chart/histogram_series.go b/vendor/github.com/wcharczuk/go-chart/v2/histogram_series.go
similarity index 100%
rename from vendor/github.com/wcharczuk/go-chart/histogram_series.go
rename to vendor/github.com/wcharczuk/go-chart/v2/histogram_series.go
diff --git a/vendor/github.com/wcharczuk/go-chart/image_writer.go b/vendor/github.com/wcharczuk/go-chart/v2/image_writer.go
similarity index 100%
rename from vendor/github.com/wcharczuk/go-chart/image_writer.go
rename to vendor/github.com/wcharczuk/go-chart/v2/image_writer.go
diff --git a/vendor/github.com/wcharczuk/go-chart/jet.go b/vendor/github.com/wcharczuk/go-chart/v2/jet.go
similarity index 93%
rename from vendor/github.com/wcharczuk/go-chart/jet.go
rename to vendor/github.com/wcharczuk/go-chart/v2/jet.go
index a948525..cce8c85 100644
--- a/vendor/github.com/wcharczuk/go-chart/jet.go
+++ b/vendor/github.com/wcharczuk/go-chart/v2/jet.go
@@ -1,6 +1,6 @@
package chart
-import "github.com/wcharczuk/go-chart/drawing"
+import "github.com/wcharczuk/go-chart/v2/drawing"
// Jet is a color map provider based on matlab's jet color map.
func Jet(v, vmin, vmax float64) drawing.Color {
diff --git a/vendor/github.com/wcharczuk/go-chart/last_value_annotation_series.go b/vendor/github.com/wcharczuk/go-chart/v2/last_value_annotation_series.go
similarity index 81%
rename from vendor/github.com/wcharczuk/go-chart/last_value_annotation_series.go
rename to vendor/github.com/wcharczuk/go-chart/v2/last_value_annotation_series.go
index f3d4b46..550c367 100644
--- a/vendor/github.com/wcharczuk/go-chart/last_value_annotation_series.go
+++ b/vendor/github.com/wcharczuk/go-chart/v2/last_value_annotation_series.go
@@ -2,8 +2,8 @@ package chart
import "fmt"
-// LastValueAnnotation returns an annotation series of just the last value of a value provider.
-func LastValueAnnotation(innerSeries ValuesProvider, vfs ...ValueFormatter) AnnotationSeries {
+// LastValueAnnotationSeries returns an annotation series of just the last value of a value provider.
+func LastValueAnnotationSeries(innerSeries ValuesProvider, vfs ...ValueFormatter) AnnotationSeries {
var vf ValueFormatter
if len(vfs) > 0 {
vf = vfs[0]
diff --git a/vendor/github.com/wcharczuk/go-chart/legend.go b/vendor/github.com/wcharczuk/go-chart/v2/legend.go
similarity index 94%
rename from vendor/github.com/wcharczuk/go-chart/legend.go
rename to vendor/github.com/wcharczuk/go-chart/v2/legend.go
index 42c11a3..fbd48ed 100644
--- a/vendor/github.com/wcharczuk/go-chart/legend.go
+++ b/vendor/github.com/wcharczuk/go-chart/v2/legend.go
@@ -1,8 +1,7 @@
package chart
import (
- "github.com/wcharczuk/go-chart/drawing"
- "github.com/wcharczuk/go-chart/util"
+ "github.com/wcharczuk/go-chart/v2/drawing"
)
// Legend returns a legend renderable function.
@@ -36,7 +35,7 @@ func Legend(c *Chart, userDefaults ...Style) Renderable {
var labels []string
var lines []Style
for index, s := range c.Series {
- if s.GetStyle().IsZero() || s.GetStyle().Show {
+ if !s.GetStyle().Hidden {
if _, isAnnotationSeries := s.(AnnotationSeries); !isAnnotationSeries {
labels = append(labels, s.GetName())
lines = append(lines, s.GetStyle().InheritFrom(c.styleDefaultsSeries(index)))
@@ -69,7 +68,7 @@ func Legend(c *Chart, userDefaults ...Style) Renderable {
}
legendContent.Bottom += tb.Height()
right := legendContent.Left + tb.Width() + lineTextGap + lineLengthMinimum
- legendContent.Right = util.Math.MaxInt(legendContent.Right, right)
+ legendContent.Right = MaxInt(legendContent.Right, right)
labelCount++
}
}
@@ -150,7 +149,7 @@ func LegendThin(c *Chart, userDefaults ...Style) Renderable {
var labels []string
var lines []Style
for index, s := range c.Series {
- if s.GetStyle().IsZero() || s.GetStyle().Show {
+ if !s.GetStyle().Hidden {
if _, isAnnotationSeries := s.(AnnotationSeries); !isAnnotationSeries {
labels = append(labels, s.GetName())
lines = append(lines, s.GetStyle().InheritFrom(c.styleDefaultsSeries(index)))
@@ -164,8 +163,8 @@ func LegendThin(c *Chart, userDefaults ...Style) Renderable {
for x := 0; x < len(labels); x++ {
if len(labels[x]) > 0 {
textBox = r.MeasureText(labels[x])
- textHeight = util.Math.MaxInt(textBox.Height(), textHeight)
- textWidth = util.Math.MaxInt(textBox.Width(), textWidth)
+ textHeight = MaxInt(textBox.Height(), textHeight)
+ textWidth = MaxInt(textBox.Width(), textWidth)
}
}
@@ -248,7 +247,7 @@ func LegendLeft(c *Chart, userDefaults ...Style) Renderable {
var labels []string
var lines []Style
for index, s := range c.Series {
- if s.GetStyle().IsZero() || s.GetStyle().Show {
+ if !s.GetStyle().Hidden {
if _, isAnnotationSeries := s.(AnnotationSeries); !isAnnotationSeries {
labels = append(labels, s.GetName())
lines = append(lines, s.GetStyle().InheritFrom(c.styleDefaultsSeries(index)))
@@ -281,7 +280,7 @@ func LegendLeft(c *Chart, userDefaults ...Style) Renderable {
}
legendContent.Bottom += tb.Height()
right := legendContent.Left + tb.Width() + lineTextGap + lineLengthMinimum
- legendContent.Right = util.Math.MaxInt(legendContent.Right, right)
+ legendContent.Right = MaxInt(legendContent.Right, right)
labelCount++
}
}
diff --git a/vendor/github.com/wcharczuk/go-chart/v2/linear_coefficient_provider.go b/vendor/github.com/wcharczuk/go-chart/v2/linear_coefficient_provider.go
new file mode 100644
index 0000000..9701f6b
--- /dev/null
+++ b/vendor/github.com/wcharczuk/go-chart/v2/linear_coefficient_provider.go
@@ -0,0 +1,42 @@
+package chart
+
+// LinearCoefficientProvider is a type that returns linear cofficients.
+type LinearCoefficientProvider interface {
+ Coefficients() (m, b, stdev, avg float64)
+}
+
+// LinearCoefficients returns a fixed linear coefficient pair.
+func LinearCoefficients(m, b float64) LinearCoefficientSet {
+ return LinearCoefficientSet{
+ M: m,
+ B: b,
+ }
+}
+
+// NormalizedLinearCoefficients returns a fixed linear coefficient pair.
+func NormalizedLinearCoefficients(m, b, stdev, avg float64) LinearCoefficientSet {
+ return LinearCoefficientSet{
+ M: m,
+ B: b,
+ StdDev: stdev,
+ Avg: avg,
+ }
+}
+
+// LinearCoefficientSet is the m and b values for the linear equation in the form:
+// y = (m*x) + b
+type LinearCoefficientSet struct {
+ M float64
+ B float64
+ StdDev float64
+ Avg float64
+}
+
+// Coefficients returns the coefficients.
+func (lcs LinearCoefficientSet) Coefficients() (m, b, stdev, avg float64) {
+ m = lcs.M
+ b = lcs.B
+ stdev = lcs.StdDev
+ avg = lcs.Avg
+ return
+}
diff --git a/vendor/github.com/wcharczuk/go-chart/linear_regression_series.go b/vendor/github.com/wcharczuk/go-chart/v2/linear_regression_series.go
similarity index 70%
rename from vendor/github.com/wcharczuk/go-chart/linear_regression_series.go
rename to vendor/github.com/wcharczuk/go-chart/v2/linear_regression_series.go
index 13c3cb0..8ff8b1a 100644
--- a/vendor/github.com/wcharczuk/go-chart/linear_regression_series.go
+++ b/vendor/github.com/wcharczuk/go-chart/v2/linear_regression_series.go
@@ -2,9 +2,14 @@ package chart
import (
"fmt"
+)
- "github.com/wcharczuk/go-chart/seq"
- util "github.com/wcharczuk/go-chart/util"
+// Interface Assertions.
+var (
+ _ Series = (*LinearRegressionSeries)(nil)
+ _ FirstValuesProvider = (*LinearRegressionSeries)(nil)
+ _ LastValuesProvider = (*LinearRegressionSeries)(nil)
+ _ LinearCoefficientProvider = (*LinearRegressionSeries)(nil)
)
// LinearRegressionSeries is a series that plots the n-nearest neighbors
@@ -24,6 +29,19 @@ type LinearRegressionSeries struct {
stddevx float64
}
+// Coefficients returns the linear coefficients for the series.
+func (lrs LinearRegressionSeries) Coefficients() (m, b, stdev, avg float64) {
+ if lrs.IsZero() {
+ lrs.computeCoefficients()
+ }
+
+ m = lrs.m
+ b = lrs.b
+ stdev = lrs.stddevx
+ avg = lrs.avgx
+ return
+}
+
// GetName returns the name of the time series.
func (lrs LinearRegressionSeries) GetName() string {
return lrs.Name
@@ -41,7 +59,7 @@ func (lrs LinearRegressionSeries) GetYAxis() YAxisType {
// Len returns the number of elements in the series.
func (lrs LinearRegressionSeries) Len() int {
- return util.Math.MinInt(lrs.GetLimit(), lrs.InnerSeries.Len()-lrs.GetOffset())
+ return MinInt(lrs.GetLimit(), lrs.InnerSeries.Len()-lrs.GetOffset())
}
// GetLimit returns the window size.
@@ -56,7 +74,7 @@ func (lrs LinearRegressionSeries) GetLimit() int {
func (lrs LinearRegressionSeries) GetEndIndex() int {
windowEnd := lrs.GetOffset() + lrs.GetLimit()
innerSeriesLastIndex := lrs.InnerSeries.Len() - 1
- return util.Math.MinInt(windowEnd, innerSeriesLastIndex)
+ return MinInt(windowEnd, innerSeriesLastIndex)
}
// GetOffset returns the data offset.
@@ -72,22 +90,35 @@ func (lrs *LinearRegressionSeries) GetValues(index int) (x, y float64) {
if lrs.InnerSeries == nil || lrs.InnerSeries.Len() == 0 {
return
}
- if lrs.m == 0 && lrs.b == 0 {
+ if lrs.IsZero() {
lrs.computeCoefficients()
}
offset := lrs.GetOffset()
- effectiveIndex := util.Math.MinInt(index+offset, lrs.InnerSeries.Len())
+ effectiveIndex := MinInt(index+offset, lrs.InnerSeries.Len())
x, y = lrs.InnerSeries.GetValues(effectiveIndex)
y = (lrs.m * lrs.normalize(x)) + lrs.b
return
}
+// GetFirstValues computes the first linear regression value.
+func (lrs *LinearRegressionSeries) GetFirstValues() (x, y float64) {
+ if lrs.InnerSeries == nil || lrs.InnerSeries.Len() == 0 {
+ return
+ }
+ if lrs.IsZero() {
+ lrs.computeCoefficients()
+ }
+ x, y = lrs.InnerSeries.GetValues(0)
+ y = (lrs.m * lrs.normalize(x)) + lrs.b
+ return
+}
+
// GetLastValues computes the last linear regression value.
func (lrs *LinearRegressionSeries) GetLastValues() (x, y float64) {
if lrs.InnerSeries == nil || lrs.InnerSeries.Len() == 0 {
return
}
- if lrs.m == 0 && lrs.b == 0 {
+ if lrs.IsZero() {
lrs.computeCoefficients()
}
endIndex := lrs.GetEndIndex()
@@ -96,6 +127,29 @@ func (lrs *LinearRegressionSeries) GetLastValues() (x, y float64) {
return
}
+// Render renders the series.
+func (lrs *LinearRegressionSeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
+ style := lrs.Style.InheritFrom(defaults)
+ Draw.LineSeries(r, canvasBox, xrange, yrange, style, lrs)
+}
+
+// Validate validates the series.
+func (lrs *LinearRegressionSeries) Validate() error {
+ if lrs.InnerSeries == nil {
+ return fmt.Errorf("linear regression series requires InnerSeries to be set")
+ }
+ return nil
+}
+
+// IsZero returns if we've computed the coefficients or not.
+func (lrs *LinearRegressionSeries) IsZero() bool {
+ return lrs.m == 0 && lrs.b == 0
+}
+
+//
+// internal helpers
+//
+
func (lrs *LinearRegressionSeries) normalize(xvalue float64) float64 {
return (xvalue - lrs.avgx) / lrs.stddevx
}
@@ -107,14 +161,14 @@ func (lrs *LinearRegressionSeries) computeCoefficients() {
p := float64(endIndex - startIndex)
- xvalues := seq.NewBufferWithCapacity(lrs.Len())
+ xvalues := NewValueBufferWithCapacity(lrs.Len())
for index := startIndex; index < endIndex; index++ {
x, _ := lrs.InnerSeries.GetValues(index)
xvalues.Enqueue(x)
}
- lrs.avgx = seq.Seq{Provider: xvalues}.Average()
- lrs.stddevx = seq.Seq{Provider: xvalues}.StdDev()
+ lrs.avgx = Seq{xvalues}.Average()
+ lrs.stddevx = Seq{xvalues}.StdDev()
var sumx, sumy, sumxx, sumxy float64
for index := startIndex; index < endIndex; index++ {
@@ -131,17 +185,3 @@ func (lrs *LinearRegressionSeries) computeCoefficients() {
lrs.m = (p*sumxy - sumx*sumy) / (p*sumxx - sumx*sumx)
lrs.b = (sumy / p) - (lrs.m * sumx / p)
}
-
-// Render renders the series.
-func (lrs *LinearRegressionSeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
- style := lrs.Style.InheritFrom(defaults)
- Draw.LineSeries(r, canvasBox, xrange, yrange, style, lrs)
-}
-
-// Validate validates the series.
-func (lrs *LinearRegressionSeries) Validate() error {
- if lrs.InnerSeries == nil {
- return fmt.Errorf("linear regression series requires InnerSeries to be set")
- }
- return nil
-}
diff --git a/vendor/github.com/wcharczuk/go-chart/v2/linear_sequence.go b/vendor/github.com/wcharczuk/go-chart/v2/linear_sequence.go
new file mode 100644
index 0000000..dda761b
--- /dev/null
+++ b/vendor/github.com/wcharczuk/go-chart/v2/linear_sequence.go
@@ -0,0 +1,73 @@
+package chart
+
+// LinearRange returns an array of values representing the range from start to end, incremented by 1.0.
+func LinearRange(start, end float64) []float64 {
+ return Seq{NewLinearSequence().WithStart(start).WithEnd(end).WithStep(1.0)}.Values()
+}
+
+// LinearRangeWithStep returns the array values of a linear seq with a given start, end and optional step.
+func LinearRangeWithStep(start, end, step float64) []float64 {
+ return Seq{NewLinearSequence().WithStart(start).WithEnd(end).WithStep(step)}.Values()
+}
+
+// NewLinearSequence returns a new linear generator.
+func NewLinearSequence() *LinearSeq {
+ return &LinearSeq{step: 1.0}
+}
+
+// LinearSeq is a stepwise generator.
+type LinearSeq struct {
+ start float64
+ end float64
+ step float64
+}
+
+// Start returns the start value.
+func (lg LinearSeq) Start() float64 {
+ return lg.start
+}
+
+// End returns the end value.
+func (lg LinearSeq) End() float64 {
+ return lg.end
+}
+
+// Step returns the step value.
+func (lg LinearSeq) Step() float64 {
+ return lg.step
+}
+
+// Len returns the number of elements in the seq.
+func (lg LinearSeq) Len() int {
+ if lg.start < lg.end {
+ return int((lg.end-lg.start)/lg.step) + 1
+ }
+ return int((lg.start-lg.end)/lg.step) + 1
+}
+
+// GetValue returns the value at a given index.
+func (lg LinearSeq) GetValue(index int) float64 {
+ fi := float64(index)
+ if lg.start < lg.end {
+ return lg.start + (fi * lg.step)
+ }
+ return lg.start - (fi * lg.step)
+}
+
+// WithStart sets the start and returns the linear generator.
+func (lg *LinearSeq) WithStart(start float64) *LinearSeq {
+ lg.start = start
+ return lg
+}
+
+// WithEnd sets the end and returns the linear generator.
+func (lg *LinearSeq) WithEnd(end float64) *LinearSeq {
+ lg.end = end
+ return lg
+}
+
+// WithStep sets the step and returns the linear generator.
+func (lg *LinearSeq) WithStep(step float64) *LinearSeq {
+ lg.step = step
+ return lg
+}
diff --git a/vendor/github.com/wcharczuk/go-chart/v2/linear_series.go b/vendor/github.com/wcharczuk/go-chart/v2/linear_series.go
new file mode 100644
index 0000000..89afa93
--- /dev/null
+++ b/vendor/github.com/wcharczuk/go-chart/v2/linear_series.go
@@ -0,0 +1,119 @@
+package chart
+
+import (
+ "fmt"
+)
+
+// Interface Assertions.
+var (
+ _ Series = (*LinearSeries)(nil)
+ _ FirstValuesProvider = (*LinearSeries)(nil)
+ _ LastValuesProvider = (*LinearSeries)(nil)
+)
+
+// LinearSeries is a series that plots a line in a given domain.
+type LinearSeries struct {
+ Name string
+ Style Style
+ YAxis YAxisType
+
+ XValues []float64
+ InnerSeries LinearCoefficientProvider
+
+ m float64
+ b float64
+ stdev float64
+ avg float64
+}
+
+// GetName returns the name of the time series.
+func (ls LinearSeries) GetName() string {
+ return ls.Name
+}
+
+// GetStyle returns the line style.
+func (ls LinearSeries) GetStyle() Style {
+ return ls.Style
+}
+
+// GetYAxis returns which YAxis the series draws on.
+func (ls LinearSeries) GetYAxis() YAxisType {
+ return ls.YAxis
+}
+
+// Len returns the number of elements in the series.
+func (ls LinearSeries) Len() int {
+ return len(ls.XValues)
+}
+
+// GetEndIndex returns the effective limit end.
+func (ls LinearSeries) GetEndIndex() int {
+ return len(ls.XValues) - 1
+}
+
+// GetValues gets a value at a given index.
+func (ls *LinearSeries) GetValues(index int) (x, y float64) {
+ if ls.InnerSeries == nil || len(ls.XValues) == 0 {
+ return
+ }
+ if ls.IsZero() {
+ ls.computeCoefficients()
+ }
+ x = ls.XValues[index]
+ y = (ls.m * ls.normalize(x)) + ls.b
+ return
+}
+
+// GetFirstValues computes the first linear regression value.
+func (ls *LinearSeries) GetFirstValues() (x, y float64) {
+ if ls.InnerSeries == nil || len(ls.XValues) == 0 {
+ return
+ }
+ if ls.IsZero() {
+ ls.computeCoefficients()
+ }
+ x, y = ls.GetValues(0)
+ return
+}
+
+// GetLastValues computes the last linear regression value.
+func (ls *LinearSeries) GetLastValues() (x, y float64) {
+ if ls.InnerSeries == nil || len(ls.XValues) == 0 {
+ return
+ }
+ if ls.IsZero() {
+ ls.computeCoefficients()
+ }
+ x, y = ls.GetValues(ls.GetEndIndex())
+ return
+}
+
+// Render renders the series.
+func (ls *LinearSeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
+ Draw.LineSeries(r, canvasBox, xrange, yrange, ls.Style.InheritFrom(defaults), ls)
+}
+
+// Validate validates the series.
+func (ls LinearSeries) Validate() error {
+ if ls.InnerSeries == nil {
+ return fmt.Errorf("linear regression series requires InnerSeries to be set")
+ }
+ return nil
+}
+
+// IsZero returns if the linear series has computed coefficients or not.
+func (ls LinearSeries) IsZero() bool {
+ return ls.m == 0 && ls.b == 0
+}
+
+// computeCoefficients computes the `m` and `b` terms in the linear formula given by `y = mx+b`.
+func (ls *LinearSeries) computeCoefficients() {
+ ls.m, ls.b, ls.stdev, ls.avg = ls.InnerSeries.Coefficients()
+}
+
+func (ls *LinearSeries) normalize(xvalue float64) float64 {
+ if ls.avg > 0 && ls.stdev > 0 {
+ return (xvalue - ls.avg) / ls.stdev
+ }
+ return xvalue
+}
diff --git a/vendor/github.com/wcharczuk/go-chart/v2/logger.go b/vendor/github.com/wcharczuk/go-chart/v2/logger.go
new file mode 100644
index 0000000..28fc003
--- /dev/null
+++ b/vendor/github.com/wcharczuk/go-chart/v2/logger.go
@@ -0,0 +1,148 @@
+package chart
+
+import (
+ "fmt"
+ "io"
+ "os"
+ "time"
+)
+
+var (
+ _ Logger = (*StdoutLogger)(nil)
+)
+
+// NewLogger returns a new logger.
+func NewLogger(options ...LoggerOption) Logger {
+ stl := &StdoutLogger{
+ TimeFormat: time.RFC3339Nano,
+ Stdout: os.Stdout,
+ Stderr: os.Stderr,
+ }
+ for _, option := range options {
+ option(stl)
+ }
+ return stl
+}
+
+// Logger is a type that implements the logging interface.
+type Logger interface {
+ Info(...interface{})
+ Infof(string, ...interface{})
+ Debug(...interface{})
+ Debugf(string, ...interface{})
+ Err(error)
+ FatalErr(error)
+ Error(...interface{})
+ Errorf(string, ...interface{})
+}
+
+// Info logs an info message if the logger is set.
+func Info(log Logger, arguments ...interface{}) {
+ if log == nil {
+ return
+ }
+ log.Info(arguments...)
+}
+
+// Infof logs an info message if the logger is set.
+func Infof(log Logger, format string, arguments ...interface{}) {
+ if log == nil {
+ return
+ }
+ log.Infof(format, arguments...)
+}
+
+// Debug logs an debug message if the logger is set.
+func Debug(log Logger, arguments ...interface{}) {
+ if log == nil {
+ return
+ }
+ log.Debug(arguments...)
+}
+
+// Debugf logs an debug message if the logger is set.
+func Debugf(log Logger, format string, arguments ...interface{}) {
+ if log == nil {
+ return
+ }
+ log.Debugf(format, arguments...)
+}
+
+// LoggerOption mutates a stdout logger.
+type LoggerOption = func(*StdoutLogger)
+
+//OptLoggerStdout sets the Stdout writer.
+func OptLoggerStdout(wr io.Writer) LoggerOption {
+ return func(stl *StdoutLogger) {
+ stl.Stdout = wr
+ }
+}
+
+// OptLoggerStderr sets the Stdout writer.
+func OptLoggerStderr(wr io.Writer) LoggerOption {
+ return func(stl *StdoutLogger) {
+ stl.Stderr = wr
+ }
+}
+
+// StdoutLogger is a basic logger.
+type StdoutLogger struct {
+ TimeFormat string
+ Stdout io.Writer
+ Stderr io.Writer
+}
+
+// Info writes an info message.
+func (l *StdoutLogger) Info(arguments ...interface{}) {
+ l.Println(append([]interface{}{"[INFO]"}, arguments...)...)
+}
+
+// Infof writes an info message.
+func (l *StdoutLogger) Infof(format string, arguments ...interface{}) {
+ l.Println(append([]interface{}{"[INFO]"}, fmt.Sprintf(format, arguments...))...)
+}
+
+// Debug writes an debug message.
+func (l *StdoutLogger) Debug(arguments ...interface{}) {
+ l.Println(append([]interface{}{"[DEBUG]"}, arguments...)...)
+}
+
+// Debugf writes an debug message.
+func (l *StdoutLogger) Debugf(format string, arguments ...interface{}) {
+ l.Println(append([]interface{}{"[DEBUG]"}, fmt.Sprintf(format, arguments...))...)
+}
+
+// Error writes an error message.
+func (l *StdoutLogger) Error(arguments ...interface{}) {
+ l.Println(append([]interface{}{"[ERROR]"}, arguments...)...)
+}
+
+// Errorf writes an error message.
+func (l *StdoutLogger) Errorf(format string, arguments ...interface{}) {
+ l.Println(append([]interface{}{"[ERROR]"}, fmt.Sprintf(format, arguments...))...)
+}
+
+// Err writes an error message.
+func (l *StdoutLogger) Err(err error) {
+ if err != nil {
+ l.Println(append([]interface{}{"[ERROR]"}, err.Error())...)
+ }
+}
+
+// FatalErr writes an error message and exits.
+func (l *StdoutLogger) FatalErr(err error) {
+ if err != nil {
+ l.Println(append([]interface{}{"[FATAL]"}, err.Error())...)
+ os.Exit(1)
+ }
+}
+
+// Println prints a new message.
+func (l *StdoutLogger) Println(arguments ...interface{}) {
+ fmt.Fprintln(l.Stdout, append([]interface{}{time.Now().UTC().Format(l.TimeFormat)}, arguments...)...)
+}
+
+// Errorln prints a new message.
+func (l *StdoutLogger) Errorln(arguments ...interface{}) {
+ fmt.Fprintln(l.Stderr, append([]interface{}{time.Now().UTC().Format(l.TimeFormat)}, arguments...)...)
+}
diff --git a/vendor/github.com/wcharczuk/go-chart/macd_series.go b/vendor/github.com/wcharczuk/go-chart/v2/macd_series.go
similarity index 100%
rename from vendor/github.com/wcharczuk/go-chart/macd_series.go
rename to vendor/github.com/wcharczuk/go-chart/v2/macd_series.go
diff --git a/vendor/github.com/wcharczuk/go-chart/util/math.go b/vendor/github.com/wcharczuk/go-chart/v2/mathutil.go
similarity index 53%
rename from vendor/github.com/wcharczuk/go-chart/util/math.go
rename to vendor/github.com/wcharczuk/go-chart/v2/mathutil.go
index 73f4976..d1f07f9 100644
--- a/vendor/github.com/wcharczuk/go-chart/util/math.go
+++ b/vendor/github.com/wcharczuk/go-chart/v2/mathutil.go
@@ -1,9 +1,6 @@
-package util
+package chart
-import (
- "math"
- "time"
-)
+import "math"
const (
_pi = math.Pi
@@ -19,86 +16,143 @@ const (
_r2d = (180.0 / math.Pi)
)
-var (
- // Math contains helper methods for common math operations.
- Math = &mathUtil{}
-)
-
-type mathUtil struct{}
-
-// Max returns the maximum value of a group of floats.
-func (m mathUtil) Max(values ...float64) float64 {
+// MinMax returns the minimum and maximum of a given set of values.
+func MinMax(values ...float64) (min, max float64) {
if len(values) == 0 {
- return 0
+ return
}
- max := values[0]
- for _, v := range values {
- if max < v {
- max = v
+
+ max = values[0]
+ min = values[0]
+ var value float64
+ for index := 1; index < len(values); index++ {
+ value = values[index]
+ if value < min {
+ min = value
+ }
+ if value > max {
+ max = value
}
}
- return max
+ return
}
-// MinAndMax returns both the min and max in one pass.
-func (m mathUtil) MinAndMax(values ...float64) (min float64, max float64) {
+// MinInt returns the minimum int.
+func MinInt(values ...int) (min int) {
if len(values) == 0 {
return
}
+
min = values[0]
- max = values[0]
- for _, v := range values[1:] {
- if max < v {
- max = v
- }
- if min > v {
- min = v
+ var value int
+ for index := 1; index < len(values); index++ {
+ value = values[index]
+ if value < min {
+ min = value
}
}
return
}
-// MinAndMaxOfTime returns the min and max of a given set of times
-// in one pass.
-func (m mathUtil) MinAndMaxOfTime(values ...time.Time) (min time.Time, max time.Time) {
+// MaxInt returns the maximum int.
+func MaxInt(values ...int) (max int) {
if len(values) == 0 {
return
}
- min = values[0]
max = values[0]
-
- for _, v := range values[1:] {
- if max.Before(v) {
- max = v
- }
- if min.After(v) {
- min = v
+ var value int
+ for index := 1; index < len(values); index++ {
+ value = values[index]
+ if value > max {
+ max = value
}
}
return
}
-// GetRoundToForDelta returns a `roundTo` value for a given delta.
-func (m mathUtil) GetRoundToForDelta(delta float64) float64 {
- startingDeltaBound := math.Pow(10.0, 10.0)
- for cursor := startingDeltaBound; cursor > 0; cursor /= 10.0 {
- if delta > cursor {
- return cursor / 10.0
- }
+// AbsInt returns the absolute value of an int.
+func AbsInt(value int) int {
+ if value < 0 {
+ return -value
}
+ return value
+}
- return 0.0
+// DegreesToRadians returns degrees as radians.
+func DegreesToRadians(degrees float64) float64 {
+ return degrees * _d2r
+}
+
+// RadiansToDegrees translates a radian value to a degree value.
+func RadiansToDegrees(value float64) float64 {
+ return math.Mod(value, _2pi) * _r2d
+}
+
+// PercentToRadians converts a normalized value (0,1) to radians.
+func PercentToRadians(pct float64) float64 {
+ return DegreesToRadians(360.0 * pct)
+}
+
+// RadianAdd adds a delta to a base in radians.
+func RadianAdd(base, delta float64) float64 {
+ value := base + delta
+ if value > _2pi {
+ return math.Mod(value, _2pi)
+ } else if value < 0 {
+ return math.Mod(_2pi+value, _2pi)
+ }
+ return value
+}
+
+// DegreesAdd adds a delta to a base in radians.
+func DegreesAdd(baseDegrees, deltaDegrees float64) float64 {
+ value := baseDegrees + deltaDegrees
+ if value > _2pi {
+ return math.Mod(value, 360.0)
+ } else if value < 0 {
+ return math.Mod(360.0+value, 360.0)
+ }
+ return value
+}
+
+// DegreesToCompass returns the degree value in compass / clock orientation.
+func DegreesToCompass(deg float64) float64 {
+ return DegreesAdd(deg, -90.0)
+}
+
+// CirclePoint returns the absolute position of a circle diameter point given
+// by the radius and the theta.
+func CirclePoint(cx, cy int, radius, thetaRadians float64) (x, y int) {
+ x = cx + int(radius*math.Sin(thetaRadians))
+ y = cy - int(radius*math.Cos(thetaRadians))
+ return
+}
+
+// RotateCoordinate rotates a coordinate around a given center by a theta in radians.
+func RotateCoordinate(cx, cy, x, y int, thetaRadians float64) (rx, ry int) {
+ tempX, tempY := float64(x-cx), float64(y-cy)
+ rotatedX := tempX*math.Cos(thetaRadians) - tempY*math.Sin(thetaRadians)
+ rotatedY := tempX*math.Sin(thetaRadians) + tempY*math.Cos(thetaRadians)
+ rx = int(rotatedX) + cx
+ ry = int(rotatedY) + cy
+ return
}
// RoundUp rounds up to a given roundTo value.
-func (m mathUtil) RoundUp(value, roundTo float64) float64 {
+func RoundUp(value, roundTo float64) float64 {
+ if roundTo < 0.000000000000001 {
+ return value
+ }
d1 := math.Ceil(value / roundTo)
return d1 * roundTo
}
// RoundDown rounds down to a given roundTo value.
-func (m mathUtil) RoundDown(value, roundTo float64) float64 {
+func RoundDown(value, roundTo float64) float64 {
+ if roundTo < 0.000000000000001 {
+ return value
+ }
d1 := math.Floor(value / roundTo)
return d1 * roundTo
}
@@ -106,68 +160,30 @@ func (m mathUtil) RoundDown(value, roundTo float64) float64 {
// Normalize returns a set of numbers on the interval [0,1] for a given set of inputs.
// An example: 4,3,2,1 => 0.4, 0.3, 0.2, 0.1
// Caveat; the total may be < 1.0; there are going to be issues with irrational numbers etc.
-func (m mathUtil) Normalize(values ...float64) []float64 {
+func Normalize(values ...float64) []float64 {
var total float64
for _, v := range values {
total += v
}
output := make([]float64, len(values))
for x, v := range values {
- output[x] = m.RoundDown(v/total, 0.0001)
+ output[x] = RoundDown(v/total, 0.0001)
}
return output
}
-// MinInt returns the minimum of a set of integers.
-func (m mathUtil) MinInt(values ...int) int {
- min := math.MaxInt32
- for _, v := range values {
- if v < min {
- min = v
- }
- }
- return min
-}
-
-// MaxInt returns the maximum of a set of integers.
-func (m mathUtil) MaxInt(values ...int) int {
- max := math.MinInt32
- for _, v := range values {
- if v > max {
- max = v
- }
- }
- return max
-}
-
-// AbsInt returns the absolute value of an integer.
-func (m mathUtil) AbsInt(value int) int {
- if value < 0 {
- return -value
- }
- return value
-}
-
-// AbsInt64 returns the absolute value of a long.
-func (m mathUtil) AbsInt64(value int64) int64 {
- if value < 0 {
- return -value
- }
- return value
-}
-
// Mean returns the mean of a set of values
-func (m mathUtil) Mean(values ...float64) float64 {
- return m.Sum(values...) / float64(len(values))
+func Mean(values ...float64) float64 {
+ return Sum(values...) / float64(len(values))
}
// MeanInt returns the mean of a set of integer values.
-func (m mathUtil) MeanInt(values ...int) int {
- return m.SumInt(values...) / len(values)
+func MeanInt(values ...int) int {
+ return SumInt(values...) / len(values)
}
// Sum sums a set of values.
-func (m mathUtil) Sum(values ...float64) float64 {
+func Sum(values ...float64) float64 {
var total float64
for _, v := range values {
total += v
@@ -176,7 +192,7 @@ func (m mathUtil) Sum(values ...float64) float64 {
}
// SumInt sums a set of values.
-func (m mathUtil) SumInt(values ...int) int {
+func SumInt(values ...int) int {
var total int
for _, v := range values {
total += v
@@ -186,68 +202,51 @@ func (m mathUtil) SumInt(values ...int) int {
// PercentDifference computes the percentage difference between two values.
// The formula is (v2-v1)/v1.
-func (m mathUtil) PercentDifference(v1, v2 float64) float64 {
+func PercentDifference(v1, v2 float64) float64 {
if v1 == 0 {
return 0
}
return (v2 - v1) / v1
}
-// DegreesToRadians returns degrees as radians.
-func (m mathUtil) DegreesToRadians(degrees float64) float64 {
- return degrees * _d2r
-}
-
-// RadiansToDegrees translates a radian value to a degree value.
-func (m mathUtil) RadiansToDegrees(value float64) float64 {
- return math.Mod(value, _2pi) * _r2d
-}
+// GetRoundToForDelta returns a `roundTo` value for a given delta.
+func GetRoundToForDelta(delta float64) float64 {
+ startingDeltaBound := math.Pow(10.0, 10.0)
+ for cursor := startingDeltaBound; cursor > 0; cursor /= 10.0 {
+ if delta > cursor {
+ return cursor / 10.0
+ }
+ }
-// PercentToRadians converts a normalized value (0,1) to radians.
-func (m mathUtil) PercentToRadians(pct float64) float64 {
- return m.DegreesToRadians(360.0 * pct)
+ return 0.0
}
-// RadianAdd adds a delta to a base in radians.
-func (m mathUtil) RadianAdd(base, delta float64) float64 {
- value := base + delta
- if value > _2pi {
- return math.Mod(value, _2pi)
- } else if value < 0 {
- return math.Mod(_2pi+value, _2pi)
+// RoundPlaces rounds an input to a given places.
+func RoundPlaces(input float64, places int) (rounded float64) {
+ if math.IsNaN(input) {
+ return 0.0
}
- return value
-}
-// DegreesAdd adds a delta to a base in radians.
-func (m mathUtil) DegreesAdd(baseDegrees, deltaDegrees float64) float64 {
- value := baseDegrees + deltaDegrees
- if value > _2pi {
- return math.Mod(value, 360.0)
- } else if value < 0 {
- return math.Mod(360.0+value, 360.0)
+ sign := 1.0
+ if input < 0 {
+ sign = -1
+ input *= -1
}
- return value
-}
-// DegreesToCompass returns the degree value in compass / clock orientation.
-func (m mathUtil) DegreesToCompass(deg float64) float64 {
- return m.DegreesAdd(deg, -90.0)
-}
+ precision := math.Pow(10, float64(places))
+ digit := input * precision
+ _, decimal := math.Modf(digit)
-// CirclePoint returns the absolute position of a circle diameter point given
-// by the radius and the theta.
-func (m mathUtil) CirclePoint(cx, cy int, radius, thetaRadians float64) (x, y int) {
- x = cx + int(radius*math.Sin(thetaRadians))
- y = cy - int(radius*math.Cos(thetaRadians))
- return
+ if decimal >= 0.5 {
+ rounded = math.Ceil(digit)
+ } else {
+ rounded = math.Floor(digit)
+ }
+
+ return rounded / precision * sign
}
-func (m mathUtil) RotateCoordinate(cx, cy, x, y int, thetaRadians float64) (rx, ry int) {
- tempX, tempY := float64(x-cx), float64(y-cy)
- rotatedX := tempX*math.Cos(thetaRadians) - tempY*math.Sin(thetaRadians)
- rotatedY := tempX*math.Sin(thetaRadians) + tempY*math.Cos(thetaRadians)
- rx = int(rotatedX) + cx
- ry = int(rotatedY) + cy
- return
+func f64i(value float64) int {
+ r := RoundPlaces(value, 0)
+ return int(r)
}
diff --git a/vendor/github.com/wcharczuk/go-chart/matrix/matrix.go b/vendor/github.com/wcharczuk/go-chart/v2/matrix/matrix.go
similarity index 100%
rename from vendor/github.com/wcharczuk/go-chart/matrix/matrix.go
rename to vendor/github.com/wcharczuk/go-chart/v2/matrix/matrix.go
diff --git a/vendor/github.com/wcharczuk/go-chart/matrix/regression.go b/vendor/github.com/wcharczuk/go-chart/v2/matrix/regression.go
similarity index 100%
rename from vendor/github.com/wcharczuk/go-chart/matrix/regression.go
rename to vendor/github.com/wcharczuk/go-chart/v2/matrix/regression.go
diff --git a/vendor/github.com/wcharczuk/go-chart/matrix/util.go b/vendor/github.com/wcharczuk/go-chart/v2/matrix/util.go
similarity index 100%
rename from vendor/github.com/wcharczuk/go-chart/matrix/util.go
rename to vendor/github.com/wcharczuk/go-chart/v2/matrix/util.go
diff --git a/vendor/github.com/wcharczuk/go-chart/matrix/vector.go b/vendor/github.com/wcharczuk/go-chart/v2/matrix/vector.go
similarity index 100%
rename from vendor/github.com/wcharczuk/go-chart/matrix/vector.go
rename to vendor/github.com/wcharczuk/go-chart/v2/matrix/vector.go
diff --git a/vendor/github.com/wcharczuk/go-chart/min_max_series.go b/vendor/github.com/wcharczuk/go-chart/v2/min_max_series.go
similarity index 100%
rename from vendor/github.com/wcharczuk/go-chart/min_max_series.go
rename to vendor/github.com/wcharczuk/go-chart/v2/min_max_series.go
diff --git a/vendor/github.com/wcharczuk/go-chart/v2/parse.go b/vendor/github.com/wcharczuk/go-chart/v2/parse.go
new file mode 100644
index 0000000..5ecae0a
--- /dev/null
+++ b/vendor/github.com/wcharczuk/go-chart/v2/parse.go
@@ -0,0 +1,40 @@
+package chart
+
+import (
+ "strconv"
+ "strings"
+ "time"
+)
+
+// ParseFloats parses a list of floats.
+func ParseFloats(values ...string) ([]float64, error) {
+ var output []float64
+ var parsedValue float64
+ var err error
+ var cleaned string
+ for _, value := range values {
+ cleaned = strings.TrimSpace(strings.Replace(value, ",", "", -1))
+ if cleaned == "" {
+ continue
+ }
+ if parsedValue, err = strconv.ParseFloat(cleaned, 64); err != nil {
+ return nil, err
+ }
+ output = append(output, parsedValue)
+ }
+ return output, nil
+}
+
+// ParseTimes parses a list of times with a given format.
+func ParseTimes(layout string, values ...string) ([]time.Time, error) {
+ var output []time.Time
+ var parsedValue time.Time
+ var err error
+ for _, value := range values {
+ if parsedValue, err = time.Parse(layout, value); err != nil {
+ return nil, err
+ }
+ output = append(output, parsedValue)
+ }
+ return output, nil
+}
diff --git a/vendor/github.com/wcharczuk/go-chart/v2/percent_change_series.go b/vendor/github.com/wcharczuk/go-chart/v2/percent_change_series.go
new file mode 100644
index 0000000..3767893
--- /dev/null
+++ b/vendor/github.com/wcharczuk/go-chart/v2/percent_change_series.go
@@ -0,0 +1,89 @@
+package chart
+
+// Interface Assertions.
+var (
+ _ Series = (*PercentChangeSeries)(nil)
+ _ FirstValuesProvider = (*PercentChangeSeries)(nil)
+ _ LastValuesProvider = (*PercentChangeSeries)(nil)
+ _ ValueFormatterProvider = (*PercentChangeSeries)(nil)
+)
+
+// PercentChangeSeriesSource is a series that
+// can be used with a PercentChangeSeries
+type PercentChangeSeriesSource interface {
+ Series
+ FirstValuesProvider
+ LastValuesProvider
+ ValuesProvider
+ ValueFormatterProvider
+}
+
+// PercentChangeSeries applies a
+// percentage difference function to a given continuous series.
+type PercentChangeSeries struct {
+ Name string
+ Style Style
+ YAxis YAxisType
+ InnerSeries PercentChangeSeriesSource
+}
+
+// GetName returns the name of the time series.
+func (pcs PercentChangeSeries) GetName() string {
+ return pcs.Name
+}
+
+// GetStyle returns the line style.
+func (pcs PercentChangeSeries) GetStyle() Style {
+ return pcs.Style
+}
+
+// Len implements part of Series.
+func (pcs PercentChangeSeries) Len() int {
+ return pcs.InnerSeries.Len()
+}
+
+// GetFirstValues implements FirstValuesProvider.
+func (pcs PercentChangeSeries) GetFirstValues() (x, y float64) {
+ return pcs.InnerSeries.GetFirstValues()
+}
+
+// GetValues gets x, y values at a given index.
+func (pcs PercentChangeSeries) GetValues(index int) (x, y float64) {
+ _, fy := pcs.InnerSeries.GetFirstValues()
+ x0, y0 := pcs.InnerSeries.GetValues(index)
+ x = x0
+ y = PercentDifference(fy, y0)
+ return
+}
+
+// GetValueFormatters returns value formatter defaults for the series.
+func (pcs PercentChangeSeries) GetValueFormatters() (x, y ValueFormatter) {
+ x, _ = pcs.InnerSeries.GetValueFormatters()
+ y = PercentValueFormatter
+ return
+}
+
+// GetYAxis returns which YAxis the series draws on.
+func (pcs PercentChangeSeries) GetYAxis() YAxisType {
+ return pcs.YAxis
+}
+
+// GetLastValues gets the last values.
+func (pcs PercentChangeSeries) GetLastValues() (x, y float64) {
+ _, fy := pcs.InnerSeries.GetFirstValues()
+ x0, y0 := pcs.InnerSeries.GetLastValues()
+ x = x0
+ y = PercentDifference(fy, y0)
+ return
+}
+
+// Render renders the series.
+func (pcs PercentChangeSeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
+ style := pcs.Style.InheritFrom(defaults)
+ Draw.LineSeries(r, canvasBox, xrange, yrange, style, pcs)
+}
+
+// Validate validates the series.
+func (pcs PercentChangeSeries) Validate() error {
+ return pcs.InnerSeries.Validate()
+}
diff --git a/vendor/github.com/wcharczuk/go-chart/pie_chart.go b/vendor/github.com/wcharczuk/go-chart/v2/pie_chart.go
similarity index 87%
rename from vendor/github.com/wcharczuk/go-chart/pie_chart.go
rename to vendor/github.com/wcharczuk/go-chart/v2/pie_chart.go
index 6d5d75e..49b551f 100644
--- a/vendor/github.com/wcharczuk/go-chart/pie_chart.go
+++ b/vendor/github.com/wcharczuk/go-chart/v2/pie_chart.go
@@ -4,16 +4,8 @@ import (
"errors"
"fmt"
"io"
- "math"
"github.com/golang/freetype/truetype"
- "github.com/wcharczuk/go-chart/util"
-)
-
-const (
- _pi = math.Pi
- _pi2 = math.Pi / 2.0
- _pi4 = math.Pi / 4.0
)
// PieChart is a chart that draws sections of a circle based on percentages.
@@ -124,33 +116,40 @@ func (pc PieChart) drawCanvas(r Renderer, canvasBox Box) {
}
func (pc PieChart) drawTitle(r Renderer) {
- if len(pc.Title) > 0 && pc.TitleStyle.Show {
+ if len(pc.Title) > 0 && !pc.TitleStyle.Hidden {
Draw.TextWithin(r, pc.Title, pc.Box(), pc.styleDefaultsTitle())
}
}
func (pc PieChart) drawSlices(r Renderer, canvasBox Box, values []Value) {
cx, cy := canvasBox.Center()
- diameter := util.Math.MinInt(canvasBox.Width(), canvasBox.Height())
+ diameter := MinInt(canvasBox.Width(), canvasBox.Height())
radius := float64(diameter >> 1)
labelRadius := (radius * 2.0) / 3.0
// draw the pie slices
var rads, delta, delta2, total float64
var lx, ly int
- for index, v := range values {
- v.Style.InheritFrom(pc.stylePieChartValue(index)).WriteToRenderer(r)
+ if len(values) == 1 {
+ pc.stylePieChartValue(0).WriteToRenderer(r)
r.MoveTo(cx, cy)
- rads = util.Math.PercentToRadians(total)
- delta = util.Math.PercentToRadians(v.Value)
+ r.Circle(radius, cx, cy)
+ } else {
+ for index, v := range values {
+ v.Style.InheritFrom(pc.stylePieChartValue(index)).WriteToRenderer(r)
- r.ArcTo(cx, cy, radius, radius, rads, delta)
+ r.MoveTo(cx, cy)
+ rads = PercentToRadians(total)
+ delta = PercentToRadians(v.Value)
- r.LineTo(cx, cy)
- r.Close()
- r.FillStroke()
- total = total + v.Value
+ r.ArcTo(cx, cy, radius, radius, rads, delta)
+
+ r.LineTo(cx, cy)
+ r.Close()
+ r.FillStroke()
+ total = total + v.Value
+ }
}
// draw the labels
@@ -158,14 +157,21 @@ func (pc PieChart) drawSlices(r Renderer, canvasBox Box, values []Value) {
for index, v := range values {
v.Style.InheritFrom(pc.stylePieChartValue(index)).WriteToRenderer(r)
if len(v.Label) > 0 {
- delta2 = util.Math.PercentToRadians(total + (v.Value / 2.0))
- delta2 = util.Math.RadianAdd(delta2, _pi2)
- lx, ly = util.Math.CirclePoint(cx, cy, labelRadius, delta2)
+ delta2 = PercentToRadians(total + (v.Value / 2.0))
+ delta2 = RadianAdd(delta2, _pi2)
+ lx, ly = CirclePoint(cx, cy, labelRadius, delta2)
tb := r.MeasureText(v.Label)
lx = lx - (tb.Width() >> 1)
ly = ly + (tb.Height() >> 1)
+ if lx < 0 {
+ lx = 0
+ }
+ if ly < 0 {
+ lx = 0
+ }
+
r.Text(v.Label, lx, ly)
}
total = total + v.Value
@@ -185,7 +191,7 @@ func (pc PieChart) getDefaultCanvasBox() Box {
}
func (pc PieChart) getCircleAdjustedCanvasBox(canvasBox Box) Box {
- circleDiameter := util.Math.MinInt(canvasBox.Width(), canvasBox.Height())
+ circleDiameter := MinInt(canvasBox.Width(), canvasBox.Height())
square := Box{
Right: circleDiameter,
@@ -231,7 +237,7 @@ func (pc PieChart) stylePieChartValue(index int) Style {
}
func (pc PieChart) getScaledFontSize() float64 {
- effectiveDimension := util.Math.MinInt(pc.GetWidth(), pc.GetHeight())
+ effectiveDimension := MinInt(pc.GetWidth(), pc.GetHeight())
if effectiveDimension >= 2048 {
return 48.0
} else if effectiveDimension >= 1024 {
@@ -270,7 +276,7 @@ func (pc PieChart) styleDefaultsTitle() Style {
}
func (pc PieChart) getTitleFontSize() float64 {
- effectiveDimension := util.Math.MinInt(pc.GetWidth(), pc.GetHeight())
+ effectiveDimension := MinInt(pc.GetWidth(), pc.GetHeight())
if effectiveDimension >= 2048 {
return 48
} else if effectiveDimension >= 1024 {
diff --git a/vendor/github.com/wcharczuk/go-chart/polynomial_regression_series.go b/vendor/github.com/wcharczuk/go-chart/v2/polynomial_regression_series.go
similarity index 81%
rename from vendor/github.com/wcharczuk/go-chart/polynomial_regression_series.go
rename to vendor/github.com/wcharczuk/go-chart/v2/polynomial_regression_series.go
index 506a4cb..22cd3c1 100644
--- a/vendor/github.com/wcharczuk/go-chart/polynomial_regression_series.go
+++ b/vendor/github.com/wcharczuk/go-chart/v2/polynomial_regression_series.go
@@ -4,8 +4,14 @@ import (
"fmt"
"math"
- "github.com/wcharczuk/go-chart/matrix"
- util "github.com/wcharczuk/go-chart/util"
+ "github.com/wcharczuk/go-chart/v2/matrix"
+)
+
+// Interface Assertions.
+var (
+ _ Series = (*PolynomialRegressionSeries)(nil)
+ _ FirstValuesProvider = (*PolynomialRegressionSeries)(nil)
+ _ LastValuesProvider = (*PolynomialRegressionSeries)(nil)
)
// PolynomialRegressionSeries implements a polynomial regression over a given
@@ -40,7 +46,7 @@ func (prs PolynomialRegressionSeries) GetYAxis() YAxisType {
// Len returns the number of elements in the series.
func (prs PolynomialRegressionSeries) Len() int {
- return util.Math.MinInt(prs.GetLimit(), prs.InnerSeries.Len()-prs.GetOffset())
+ return MinInt(prs.GetLimit(), prs.InnerSeries.Len()-prs.GetOffset())
}
// GetLimit returns the window size.
@@ -55,7 +61,7 @@ func (prs PolynomialRegressionSeries) GetLimit() int {
func (prs PolynomialRegressionSeries) GetEndIndex() int {
windowEnd := prs.GetOffset() + prs.GetLimit()
innerSeriesLastIndex := prs.InnerSeries.Len() - 1
- return util.Math.MinInt(windowEnd, innerSeriesLastIndex)
+ return MinInt(windowEnd, innerSeriesLastIndex)
}
// GetOffset returns the data offset.
@@ -95,12 +101,29 @@ func (prs *PolynomialRegressionSeries) GetValues(index int) (x, y float64) {
}
offset := prs.GetOffset()
- effectiveIndex := util.Math.MinInt(index+offset, prs.InnerSeries.Len())
+ effectiveIndex := MinInt(index+offset, prs.InnerSeries.Len())
x, y = prs.InnerSeries.GetValues(effectiveIndex)
y = prs.apply(x)
return
}
+// GetFirstValues computes the first poly regression value.
+func (prs *PolynomialRegressionSeries) GetFirstValues() (x, y float64) {
+ if prs.InnerSeries == nil || prs.InnerSeries.Len() == 0 {
+ return
+ }
+ if prs.coeffs == nil {
+ coeffs, err := prs.computeCoefficients()
+ if err != nil {
+ panic(err)
+ }
+ prs.coeffs = coeffs
+ }
+ x, y = prs.InnerSeries.GetValues(0)
+ y = prs.apply(x)
+ return
+}
+
// GetLastValues computes the last poly regression value.
func (prs *PolynomialRegressionSeries) GetLastValues() (x, y float64) {
if prs.InnerSeries == nil || prs.InnerSeries.Len() == 0 {
diff --git a/vendor/github.com/wcharczuk/go-chart/seq/random.go b/vendor/github.com/wcharczuk/go-chart/v2/random_sequence.go
similarity index 64%
rename from vendor/github.com/wcharczuk/go-chart/seq/random.go
rename to vendor/github.com/wcharczuk/go-chart/v2/random_sequence.go
index ea65084..45c9971 100644
--- a/vendor/github.com/wcharczuk/go-chart/seq/random.go
+++ b/vendor/github.com/wcharczuk/go-chart/v2/random_sequence.go
@@ -1,4 +1,4 @@
-package seq
+package chart
import (
"math"
@@ -6,25 +6,29 @@ import (
"time"
)
+var (
+ _ Sequence = (*RandomSeq)(nil)
+)
+
// RandomValues returns an array of random values.
func RandomValues(count int) []float64 {
- return Seq{NewRandom().WithLen(count)}.Array()
+ return Seq{NewRandomSequence().WithLen(count)}.Values()
}
// RandomValuesWithMax returns an array of random values with a given average.
func RandomValuesWithMax(count int, max float64) []float64 {
- return Seq{NewRandom().WithMax(max).WithLen(count)}.Array()
+ return Seq{NewRandomSequence().WithMax(max).WithLen(count)}.Values()
}
-// NewRandom creates a new random seq.
-func NewRandom() *Random {
- return &Random{
+// NewRandomSequence creates a new random seq.
+func NewRandomSequence() *RandomSeq {
+ return &RandomSeq{
rnd: rand.New(rand.NewSource(time.Now().Unix())),
}
}
-// Random is a random number seq generator.
-type Random struct {
+// RandomSeq is a random number seq generator.
+type RandomSeq struct {
rnd *rand.Rand
max *float64
min *float64
@@ -32,7 +36,7 @@ type Random struct {
}
// Len returns the number of elements that will be generated.
-func (r *Random) Len() int {
+func (r *RandomSeq) Len() int {
if r.len != nil {
return *r.len
}
@@ -40,7 +44,7 @@ func (r *Random) Len() int {
}
// GetValue returns the value.
-func (r *Random) GetValue(_ int) float64 {
+func (r *RandomSeq) GetValue(_ int) float64 {
if r.min != nil && r.max != nil {
var delta float64
@@ -60,29 +64,29 @@ func (r *Random) GetValue(_ int) float64 {
}
// WithLen sets a maximum len
-func (r *Random) WithLen(length int) *Random {
+func (r *RandomSeq) WithLen(length int) *RandomSeq {
r.len = &length
return r
}
// Min returns the minimum value.
-func (r Random) Min() *float64 {
+func (r RandomSeq) Min() *float64 {
return r.min
}
// WithMin sets the scale and returns the Random.
-func (r *Random) WithMin(min float64) *Random {
+func (r *RandomSeq) WithMin(min float64) *RandomSeq {
r.min = &min
return r
}
// Max returns the maximum value.
-func (r Random) Max() *float64 {
+func (r RandomSeq) Max() *float64 {
return r.max
}
// WithMax sets the average and returns the Random.
-func (r *Random) WithMax(max float64) *Random {
+func (r *RandomSeq) WithMax(max float64) *RandomSeq {
r.max = &max
return r
}
diff --git a/vendor/github.com/wcharczuk/go-chart/range.go b/vendor/github.com/wcharczuk/go-chart/v2/range.go
similarity index 100%
rename from vendor/github.com/wcharczuk/go-chart/range.go
rename to vendor/github.com/wcharczuk/go-chart/v2/range.go
diff --git a/vendor/github.com/wcharczuk/go-chart/raster_renderer.go b/vendor/github.com/wcharczuk/go-chart/v2/raster_renderer.go
similarity index 95%
rename from vendor/github.com/wcharczuk/go-chart/raster_renderer.go
rename to vendor/github.com/wcharczuk/go-chart/v2/raster_renderer.go
index 1f18309..4de2655 100644
--- a/vendor/github.com/wcharczuk/go-chart/raster_renderer.go
+++ b/vendor/github.com/wcharczuk/go-chart/v2/raster_renderer.go
@@ -7,8 +7,7 @@ import (
"math"
"github.com/golang/freetype/truetype"
- "github.com/wcharczuk/go-chart/drawing"
- "github.com/wcharczuk/go-chart/util"
+ "github.com/wcharczuk/go-chart/v2/drawing"
)
// PNG returns a new png/raster renderer.
@@ -49,6 +48,9 @@ func (rr *rasterRenderer) SetDPI(dpi float64) {
rr.gc.SetDPI(dpi)
}
+// SetClassName implements the interface method. However, PNGs have no classes.
+func (rr *rasterRenderer) SetClassName(_ string) {}
+
// SetStrokeColor implements the interface method.
func (rr *rasterRenderer) SetStrokeColor(c drawing.Color) {
rr.s.StrokeColor = c
@@ -192,7 +194,7 @@ func (rr *rasterRenderer) MeasureText(body string) Box {
return textBox
}
- return textBox.Corners().Rotate(util.Math.RadiansToDegrees(*rr.rotateRadians)).Box()
+ return textBox.Corners().Rotate(RadiansToDegrees(*rr.rotateRadians)).Box()
}
// SetTextRotation sets a text rotation.
diff --git a/vendor/github.com/wcharczuk/go-chart/renderable.go b/vendor/github.com/wcharczuk/go-chart/v2/renderable.go
similarity index 100%
rename from vendor/github.com/wcharczuk/go-chart/renderable.go
rename to vendor/github.com/wcharczuk/go-chart/v2/renderable.go
diff --git a/vendor/github.com/wcharczuk/go-chart/renderer.go b/vendor/github.com/wcharczuk/go-chart/v2/renderer.go
similarity index 94%
rename from vendor/github.com/wcharczuk/go-chart/renderer.go
rename to vendor/github.com/wcharczuk/go-chart/v2/renderer.go
index 7eb06bb..589c773 100644
--- a/vendor/github.com/wcharczuk/go-chart/renderer.go
+++ b/vendor/github.com/wcharczuk/go-chart/v2/renderer.go
@@ -4,7 +4,7 @@ import (
"io"
"github.com/golang/freetype/truetype"
- "github.com/wcharczuk/go-chart/drawing"
+ "github.com/wcharczuk/go-chart/v2/drawing"
)
// Renderer represents the basic methods required to draw a chart.
@@ -18,6 +18,9 @@ type Renderer interface {
// SetDPI sets the DPI for the renderer.
SetDPI(dpi float64)
+ // SetClassName sets the current class name.
+ SetClassName(string)
+
// SetStrokeColor sets the current stroke color.
SetStrokeColor(drawing.Color)
diff --git a/vendor/github.com/wcharczuk/go-chart/renderer_provider.go b/vendor/github.com/wcharczuk/go-chart/v2/renderer_provider.go
similarity index 100%
rename from vendor/github.com/wcharczuk/go-chart/renderer_provider.go
rename to vendor/github.com/wcharczuk/go-chart/v2/renderer_provider.go
diff --git a/vendor/github.com/wcharczuk/go-chart/roboto/roboto.go b/vendor/github.com/wcharczuk/go-chart/v2/roboto/roboto.go
similarity index 100%
rename from vendor/github.com/wcharczuk/go-chart/roboto/roboto.go
rename to vendor/github.com/wcharczuk/go-chart/v2/roboto/roboto.go
diff --git a/vendor/github.com/wcharczuk/go-chart/seq/sequence.go b/vendor/github.com/wcharczuk/go-chart/v2/seq.go
similarity index 84%
rename from vendor/github.com/wcharczuk/go-chart/seq/sequence.go
rename to vendor/github.com/wcharczuk/go-chart/v2/seq.go
index dfc369a..76ac40d 100644
--- a/vendor/github.com/wcharczuk/go-chart/seq/sequence.go
+++ b/vendor/github.com/wcharczuk/go-chart/v2/seq.go
@@ -1,33 +1,28 @@
-package seq
+package chart
import (
"math"
"sort"
)
-// New wraps a provider with a seq.
-func New(provider Provider) Seq {
- return Seq{Provider: provider}
+// ValueSequence returns a sequence for a given values set.
+func ValueSequence(values ...float64) Seq {
+ return Seq{NewArray(values...)}
}
-// Values returns a new seq composed of a given set of values.
-func Values(values ...float64) Seq {
- return Seq{Provider: Array(values)}
-}
-
-// Provider is a provider for values for a seq.
-type Provider interface {
+// Sequence is a provider for values for a seq.
+type Sequence interface {
Len() int
GetValue(int) float64
}
// Seq is a utility wrapper for seq providers.
type Seq struct {
- Provider
+ Sequence
}
-// Array enumerates the seq into a slice.
-func (s Seq) Array() (output []float64) {
+// Values enumerates the seq into a slice.
+func (s Seq) Values() (output []float64) {
if s.Len() == 0 {
return
}
@@ -148,9 +143,30 @@ func (s Seq) Sort() Seq {
if s.Len() == 0 {
return s
}
- values := s.Array()
+ values := s.Values()
sort.Float64s(values)
- return Seq{Provider: Array(values)}
+ return Seq{Array(values)}
+}
+
+// Reverse reverses the sequence
+func (s Seq) Reverse() Seq {
+ if s.Len() == 0 {
+ return s
+ }
+
+ values := s.Values()
+ valuesLen := len(values)
+ valuesLen1 := len(values) - 1
+ valuesLen2 := valuesLen >> 1
+ var i, j float64
+ for index := 0; index < valuesLen2; index++ {
+ i = values[index]
+ j = values[valuesLen1-index]
+ values[index] = j
+ values[valuesLen1-index] = i
+ }
+
+ return Seq{Array(values)}
}
// Median returns the median or middle value in the sorted seq.
@@ -255,5 +271,5 @@ func (s Seq) Normalize() Seq {
output[i] = (s.GetValue(i) - min) / delta
}
- return Seq{Provider: Array(output)}
+ return Seq{Array(output)}
}
diff --git a/vendor/github.com/wcharczuk/go-chart/series.go b/vendor/github.com/wcharczuk/go-chart/v2/series.go
similarity index 100%
rename from vendor/github.com/wcharczuk/go-chart/series.go
rename to vendor/github.com/wcharczuk/go-chart/v2/series.go
diff --git a/vendor/github.com/wcharczuk/go-chart/sma_series.go b/vendor/github.com/wcharczuk/go-chart/v2/sma_series.go
similarity index 83%
rename from vendor/github.com/wcharczuk/go-chart/sma_series.go
rename to vendor/github.com/wcharczuk/go-chart/v2/sma_series.go
index 396ecaa..b952c0a 100644
--- a/vendor/github.com/wcharczuk/go-chart/sma_series.go
+++ b/vendor/github.com/wcharczuk/go-chart/v2/sma_series.go
@@ -2,8 +2,6 @@ package chart
import (
"fmt"
-
- util "github.com/wcharczuk/go-chart/util"
)
const (
@@ -11,6 +9,13 @@ const (
DefaultSimpleMovingAveragePeriod = 16
)
+// Interface Assertions.
+var (
+ _ Series = (*SMASeries)(nil)
+ _ FirstValuesProvider = (*SMASeries)(nil)
+ _ LastValuesProvider = (*SMASeries)(nil)
+)
+
// SMASeries is a computed series.
type SMASeries struct {
Name string
@@ -63,6 +68,17 @@ func (sma SMASeries) GetValues(index int) (x, y float64) {
return
}
+// GetFirstValues computes the first moving average value.
+func (sma SMASeries) GetFirstValues() (x, y float64) {
+ if sma.InnerSeries == nil || sma.InnerSeries.Len() == 0 {
+ return
+ }
+ px, _ := sma.InnerSeries.GetValues(0)
+ x = px
+ y = sma.getAverage(0)
+ return
+}
+
// GetLastValues computes the last moving average value but walking back window size samples,
// and recomputing the last moving average chunk.
func (sma SMASeries) GetLastValues() (x, y float64) {
@@ -78,7 +94,7 @@ func (sma SMASeries) GetLastValues() (x, y float64) {
func (sma SMASeries) getAverage(index int) float64 {
period := sma.GetPeriod()
- floor := util.Math.MaxInt(0, index-period)
+ floor := MaxInt(0, index-period)
var accum float64
var count float64
for x := index; x >= floor; x-- {
diff --git a/vendor/github.com/wcharczuk/go-chart/stacked_bar_chart.go b/vendor/github.com/wcharczuk/go-chart/v2/stacked_bar_chart.go
similarity index 56%
rename from vendor/github.com/wcharczuk/go-chart/stacked_bar_chart.go
rename to vendor/github.com/wcharczuk/go-chart/v2/stacked_bar_chart.go
index 0a5e723..10aa545 100644
--- a/vendor/github.com/wcharczuk/go-chart/stacked_bar_chart.go
+++ b/vendor/github.com/wcharczuk/go-chart/v2/stacked_bar_chart.go
@@ -7,8 +7,6 @@ import (
"math"
"github.com/golang/freetype/truetype"
- "github.com/wcharczuk/go-chart/seq"
- util "github.com/wcharczuk/go-chart/util"
)
// StackedBar is a bar within a StackedBarChart.
@@ -48,6 +46,8 @@ type StackedBarChart struct {
Font *truetype.Font
defaultFont *truetype.Font
+ IsHorizontal bool
+
Bars []StackedBar
Elements []Renderable
}
@@ -115,11 +115,20 @@ func (sbc StackedBarChart) Render(rp RendererProvider, w io.Writer) error {
}
r.SetDPI(sbc.GetDPI(DefaultDPI))
- canvasBox := sbc.getAdjustedCanvasBox(r, sbc.getDefaultCanvasBox())
- sbc.drawCanvas(r, canvasBox)
- sbc.drawBars(r, canvasBox)
- sbc.drawXAxis(r, canvasBox)
- sbc.drawYAxis(r, canvasBox)
+ var canvasBox Box
+ if sbc.IsHorizontal {
+ canvasBox = sbc.getHorizontalAdjustedCanvasBox(r, sbc.getDefaultCanvasBox())
+ sbc.drawCanvas(r, canvasBox)
+ sbc.drawHorizontalBars(r, canvasBox)
+ sbc.drawHorizontalXAxis(r, canvasBox)
+ sbc.drawHorizontalYAxis(r, canvasBox)
+ } else {
+ canvasBox = sbc.getAdjustedCanvasBox(r, sbc.getDefaultCanvasBox())
+ sbc.drawCanvas(r, canvasBox)
+ sbc.drawBars(r, canvasBox)
+ sbc.drawXAxis(r, canvasBox)
+ sbc.drawYAxis(r, canvasBox)
+ }
sbc.drawTitle(r)
for _, a := range sbc.Elements {
@@ -141,6 +150,14 @@ func (sbc StackedBarChart) drawBars(r Renderer, canvasBox Box) {
}
}
+func (sbc StackedBarChart) drawHorizontalBars(r Renderer, canvasBox Box) {
+ yOffset := canvasBox.Top
+ for _, bar := range sbc.Bars {
+ sbc.drawHorizontalBar(r, canvasBox, yOffset, bar)
+ yOffset += sbc.GetBarSpacing() + bar.GetWidth()
+ }
+}
+
func (sbc StackedBarChart) drawBar(r Renderer, canvasBox Box, xoffset int, bar StackedBar) int {
barSpacing2 := sbc.GetBarSpacing() >> 1
bxl := xoffset + barSpacing2
@@ -154,17 +171,93 @@ func (sbc StackedBarChart) drawBar(r Renderer, canvasBox Box, xoffset int, bar S
Top: yoffset,
Left: bxl,
Right: bxr,
- Bottom: util.Math.MinInt(yoffset+barHeight, canvasBox.Bottom-DefaultStrokeWidth),
+ Bottom: MinInt(yoffset+barHeight, canvasBox.Bottom-DefaultStrokeWidth),
}
Draw.Box(r, barBox, bv.Style.InheritFrom(sbc.styleDefaultsStackedBarValue(index)))
yoffset += barHeight
}
+ // draw the labels
+ yoffset = canvasBox.Top
+ var lx, ly int
+ for index, bv := range normalizedBarComponents {
+ barHeight := int(math.Ceil(bv.Value * float64(canvasBox.Height())))
+
+ if len(bv.Label) > 0 {
+ lx = bxl + ((bxr - bxl) / 2)
+ ly = yoffset + (barHeight / 2)
+
+ bv.Style.InheritFrom(sbc.styleDefaultsStackedBarValue(index)).WriteToRenderer(r)
+ tb := r.MeasureText(bv.Label)
+ lx = lx - (tb.Width() >> 1)
+ ly = ly + (tb.Height() >> 1)
+
+ if lx < 0 {
+ lx = 0
+ }
+ if ly < 0 {
+ lx = 0
+ }
+
+ r.Text(bv.Label, lx, ly)
+ }
+ yoffset += barHeight
+ }
+
return bxr
}
+func (sbc StackedBarChart) drawHorizontalBar(r Renderer, canvasBox Box, yoffset int, bar StackedBar) {
+ halfBarSpacing := sbc.GetBarSpacing() >> 1
+
+ boxTop := yoffset + halfBarSpacing
+ boxBottom := boxTop + bar.GetWidth()
+
+ normalizedBarComponents := Values(bar.Values).Normalize()
+
+ xOffset := canvasBox.Right
+ for index, bv := range normalizedBarComponents {
+ barHeight := int(math.Ceil(bv.Value * float64(canvasBox.Width())))
+ barBox := Box{
+ Top: boxTop,
+ Left: MinInt(xOffset-barHeight, canvasBox.Left+DefaultStrokeWidth),
+ Right: xOffset,
+ Bottom: boxBottom,
+ }
+ Draw.Box(r, barBox, bv.Style.InheritFrom(sbc.styleDefaultsStackedBarValue(index)))
+ xOffset -= barHeight
+ }
+
+ // draw the labels
+ xOffset = canvasBox.Right
+ var lx, ly int
+ for index, bv := range normalizedBarComponents {
+ barHeight := int(math.Ceil(bv.Value * float64(canvasBox.Width())))
+
+ if len(bv.Label) > 0 {
+ lx = xOffset - (barHeight / 2)
+ ly = boxTop + ((boxBottom - boxTop) / 2)
+
+ bv.Style.InheritFrom(sbc.styleDefaultsStackedBarValue(index)).WriteToRenderer(r)
+ tb := r.MeasureText(bv.Label)
+ lx = lx - (tb.Width() >> 1)
+ ly = ly + (tb.Height() >> 1)
+
+ if lx < 0 {
+ lx = 0
+ }
+ if ly < 0 {
+ lx = 0
+ }
+
+ r.Text(bv.Label, lx, ly)
+ }
+ xOffset -= barHeight
+ }
+}
+
func (sbc StackedBarChart) drawXAxis(r Renderer, canvasBox Box) {
- if sbc.XAxis.Show {
+ if !sbc.XAxis.Hidden {
axisStyle := sbc.XAxis.InheritFrom(sbc.styleDefaultsAxes())
axisStyle.WriteToRenderer(r)
@@ -197,8 +290,44 @@ func (sbc StackedBarChart) drawXAxis(r Renderer, canvasBox Box) {
}
}
+func (sbc StackedBarChart) drawHorizontalXAxis(r Renderer, canvasBox Box) {
+ if !sbc.XAxis.Hidden {
+ axisStyle := sbc.XAxis.InheritFrom(sbc.styleDefaultsAxes())
+ axisStyle.WriteToRenderer(r)
+ r.MoveTo(canvasBox.Left, canvasBox.Bottom)
+ r.LineTo(canvasBox.Right, canvasBox.Bottom)
+ r.Stroke()
+
+ r.MoveTo(canvasBox.Left, canvasBox.Bottom)
+ r.LineTo(canvasBox.Left, canvasBox.Bottom+DefaultVerticalTickHeight)
+ r.Stroke()
+
+ ticks := LinearRangeWithStep(0.0, 1.0, 0.2)
+ for _, t := range ticks {
+ axisStyle.GetStrokeOptions().WriteToRenderer(r)
+ tx := canvasBox.Left + int(t*float64(canvasBox.Width()))
+ r.MoveTo(tx, canvasBox.Bottom)
+ r.LineTo(tx, canvasBox.Bottom+DefaultVerticalTickHeight)
+ r.Stroke()
+
+ axisStyle.GetTextOptions().WriteToRenderer(r)
+ text := fmt.Sprintf("%0.0f%%", t*100)
+
+ textBox := r.MeasureText(text)
+ textX := tx - (textBox.Width() >> 1)
+ textY := canvasBox.Bottom + DefaultXAxisMargin + 10
+
+ if t == 1 {
+ textX = canvasBox.Right - textBox.Width()
+ }
+
+ Draw.Text(r, text, textX, textY, axisStyle)
+ }
+ }
+}
+
func (sbc StackedBarChart) drawYAxis(r Renderer, canvasBox Box) {
- if sbc.YAxis.Show {
+ if !sbc.YAxis.Hidden {
axisStyle := sbc.YAxis.InheritFrom(sbc.styleDefaultsAxes())
axisStyle.WriteToRenderer(r)
r.MoveTo(canvasBox.Right, canvasBox.Top)
@@ -209,7 +338,7 @@ func (sbc StackedBarChart) drawYAxis(r Renderer, canvasBox Box) {
r.LineTo(canvasBox.Right+DefaultHorizontalTickWidth, canvasBox.Bottom)
r.Stroke()
- ticks := seq.RangeWithStep(0.0, 1.0, 0.2)
+ ticks := LinearRangeWithStep(0.0, 1.0, 0.2)
for _, t := range ticks {
axisStyle.GetStrokeOptions().WriteToRenderer(r)
ty := canvasBox.Bottom - int(t*float64(canvasBox.Height()))
@@ -223,12 +352,44 @@ func (sbc StackedBarChart) drawYAxis(r Renderer, canvasBox Box) {
tb := r.MeasureText(text)
Draw.Text(r, text, canvasBox.Right+DefaultYAxisMargin+5, ty+(tb.Height()>>1), axisStyle)
}
+ }
+}
+
+func (sbc StackedBarChart) drawHorizontalYAxis(r Renderer, canvasBox Box) {
+ if !sbc.YAxis.Hidden {
+ axisStyle := sbc.YAxis.InheritFrom(sbc.styleDefaultsHorizontalAxes())
+ axisStyle.WriteToRenderer(r)
+
+ r.MoveTo(canvasBox.Left, canvasBox.Bottom)
+ r.LineTo(canvasBox.Left, canvasBox.Top)
+ r.Stroke()
+ r.MoveTo(canvasBox.Left, canvasBox.Bottom)
+ r.LineTo(canvasBox.Left-DefaultHorizontalTickWidth, canvasBox.Bottom)
+ r.Stroke()
+
+ cursor := canvasBox.Top
+ for _, bar := range sbc.Bars {
+ barLabelBox := Box{
+ Top: cursor,
+ Left: 0,
+ Right: canvasBox.Left - DefaultYAxisMargin,
+ Bottom: cursor + bar.GetWidth() + sbc.GetBarSpacing(),
+ }
+ if len(bar.Name) > 0 {
+ Draw.TextWithin(r, bar.Name, barLabelBox, axisStyle)
+ }
+ axisStyle.WriteToRenderer(r)
+ r.MoveTo(canvasBox.Left, barLabelBox.Bottom)
+ r.LineTo(canvasBox.Left-DefaultHorizontalTickWidth, barLabelBox.Bottom)
+ r.Stroke()
+ cursor += bar.GetWidth() + sbc.GetBarSpacing()
+ }
}
}
func (sbc StackedBarChart) drawTitle(r Renderer) {
- if len(sbc.Title) > 0 && sbc.TitleStyle.Show {
+ if len(sbc.Title) > 0 && !sbc.TitleStyle.Hidden {
r.SetFont(sbc.TitleStyle.GetFont(sbc.GetFont()))
r.SetFontColor(sbc.TitleStyle.GetFontColor(sbc.GetColorPalette().TextColor()))
titleFontSize := sbc.TitleStyle.GetFontSize(DefaultTitleFontSize)
@@ -276,7 +437,7 @@ func (sbc StackedBarChart) getAdjustedCanvasBox(r Renderer, canvasBox Box) Box {
totalWidth += bar.GetWidth() + sbc.GetBarSpacing()
}
- if sbc.XAxis.Show {
+ if !sbc.XAxis.Hidden {
xaxisHeight := DefaultVerticalTickHeight
axisStyle := sbc.XAxis.InheritFrom(sbc.styleDefaultsAxes())
@@ -294,7 +455,7 @@ func (sbc StackedBarChart) getAdjustedCanvasBox(r Renderer, canvasBox Box) Box {
lines := Text.WrapFit(r, bar.Name, barLabelBox.Width(), axisStyle)
linesBox := Text.MeasureLines(r, lines, axisStyle)
- xaxisHeight = util.Math.MaxInt(linesBox.Height()+(2*DefaultXAxisMargin), xaxisHeight)
+ xaxisHeight = MaxInt(linesBox.Height()+(2*DefaultXAxisMargin), xaxisHeight)
}
}
return Box{
@@ -313,6 +474,48 @@ func (sbc StackedBarChart) getAdjustedCanvasBox(r Renderer, canvasBox Box) Box {
}
+func (sbc StackedBarChart) getHorizontalAdjustedCanvasBox(r Renderer, canvasBox Box) Box {
+ var totalHeight int
+ for _, bar := range sbc.Bars {
+ totalHeight += bar.GetWidth() + sbc.GetBarSpacing()
+ }
+
+ if !sbc.YAxis.Hidden {
+ yAxisWidth := DefaultHorizontalTickWidth
+
+ axisStyle := sbc.YAxis.InheritFrom(sbc.styleDefaultsHorizontalAxes())
+ axisStyle.WriteToRenderer(r)
+
+ cursor := canvasBox.Top
+ for _, bar := range sbc.Bars {
+ if len(bar.Name) > 0 {
+ barLabelBox := Box{
+ Top: cursor,
+ Left: 0,
+ Right: canvasBox.Left + DefaultYAxisMargin,
+ Bottom: cursor + bar.GetWidth() + sbc.GetBarSpacing(),
+ }
+ lines := Text.WrapFit(r, bar.Name, barLabelBox.Width(), axisStyle)
+ linesBox := Text.MeasureLines(r, lines, axisStyle)
+
+ yAxisWidth = MaxInt(linesBox.Height()+(2*DefaultXAxisMargin), yAxisWidth)
+ }
+ }
+ return Box{
+ Top: canvasBox.Top,
+ Left: canvasBox.Left + yAxisWidth,
+ Right: canvasBox.Right,
+ Bottom: canvasBox.Top + totalHeight,
+ }
+ }
+ return Box{
+ Top: canvasBox.Top,
+ Left: canvasBox.Left,
+ Right: canvasBox.Right,
+ Bottom: canvasBox.Top + totalHeight,
+ }
+}
+
// Box returns the chart bounds as a box.
func (sbc StackedBarChart) Box() Box {
dpr := sbc.Background.Padding.GetRight(10)
@@ -331,6 +534,9 @@ func (sbc StackedBarChart) styleDefaultsStackedBarValue(index int) Style {
StrokeColor: sbc.GetColorPalette().GetSeriesColor(index),
StrokeWidth: 3.0,
FillColor: sbc.GetColorPalette().GetSeriesColor(index),
+ FontSize: sbc.getScaledFontSize(),
+ FontColor: sbc.GetColorPalette().TextColor(),
+ Font: sbc.GetFont(),
}
}
@@ -345,8 +551,22 @@ func (sbc StackedBarChart) styleDefaultsTitle() Style {
})
}
+func (sbc StackedBarChart) getScaledFontSize() float64 {
+ effectiveDimension := MinInt(sbc.GetWidth(), sbc.GetHeight())
+ if effectiveDimension >= 2048 {
+ return 48.0
+ } else if effectiveDimension >= 1024 {
+ return 24.0
+ } else if effectiveDimension > 512 {
+ return 18.0
+ } else if effectiveDimension > 256 {
+ return 12.0
+ }
+ return 10.0
+}
+
func (sbc StackedBarChart) getTitleFontSize() float64 {
- effectiveDimension := util.Math.MinInt(sbc.GetWidth(), sbc.GetHeight())
+ effectiveDimension := MinInt(sbc.GetWidth(), sbc.GetHeight())
if effectiveDimension >= 2048 {
return 48
} else if effectiveDimension >= 1024 {
@@ -370,6 +590,19 @@ func (sbc StackedBarChart) styleDefaultsAxes() Style {
TextWrap: TextWrapWord,
}
}
+
+func (sbc StackedBarChart) styleDefaultsHorizontalAxes() Style {
+ return Style{
+ StrokeColor: DefaultAxisColor,
+ Font: sbc.GetFont(),
+ FontSize: DefaultAxisFontSize,
+ FontColor: DefaultAxisColor,
+ TextHorizontalAlign: TextHorizontalAlignCenter,
+ TextVerticalAlign: TextVerticalAlignMiddle,
+ TextWrap: TextWrapWord,
+ }
+}
+
func (sbc StackedBarChart) styleDefaultsElements() Style {
return Style{
Font: sbc.GetFont(),
diff --git a/vendor/github.com/wcharczuk/go-chart/v2/stringutil.go b/vendor/github.com/wcharczuk/go-chart/v2/stringutil.go
new file mode 100644
index 0000000..858d447
--- /dev/null
+++ b/vendor/github.com/wcharczuk/go-chart/v2/stringutil.go
@@ -0,0 +1,57 @@
+package chart
+
+import "strings"
+
+// SplitCSV splits a corpus by the `,`, dropping leading or trailing whitespace unless quoted.
+func SplitCSV(text string) (output []string) {
+ if len(text) == 0 {
+ return
+ }
+
+ var state int
+ var word []rune
+ var opened rune
+ for _, r := range text {
+ switch state {
+ case 0: // word
+ if isQuote(r) {
+ opened = r
+ state = 1
+ } else if isCSVDelim(r) {
+ output = append(output, strings.TrimSpace(string(word)))
+ word = nil
+ } else {
+ word = append(word, r)
+ }
+ case 1: // we're in a quoted section
+ if matchesQuote(opened, r) {
+ state = 0
+ continue
+ }
+ word = append(word, r)
+ }
+ }
+
+ if len(word) > 0 {
+ output = append(output, strings.TrimSpace(string(word)))
+ }
+ return
+}
+
+func isCSVDelim(r rune) bool {
+ return r == rune(',')
+}
+
+func isQuote(r rune) bool {
+ return r == '"' || r == '\'' || r == '“' || r == '”' || r == '`'
+}
+
+func matchesQuote(a, b rune) bool {
+ if a == '“' && b == '”' {
+ return true
+ }
+ if a == '”' && b == '“' {
+ return true
+ }
+ return a == b
+}
diff --git a/vendor/github.com/wcharczuk/go-chart/style.go b/vendor/github.com/wcharczuk/go-chart/v2/style.go
similarity index 89%
rename from vendor/github.com/wcharczuk/go-chart/style.go
rename to vendor/github.com/wcharczuk/go-chart/v2/style.go
index 9d1b268..c601d6d 100644
--- a/vendor/github.com/wcharczuk/go-chart/style.go
+++ b/vendor/github.com/wcharczuk/go-chart/v2/style.go
@@ -5,8 +5,7 @@ import (
"strings"
"github.com/golang/freetype/truetype"
- "github.com/wcharczuk/go-chart/drawing"
- "github.com/wcharczuk/go-chart/util"
+ "github.com/wcharczuk/go-chart/v2/drawing"
)
const (
@@ -15,10 +14,18 @@ const (
Disabled = -1
)
-// StyleShow is a prebuilt style with the `Show` property set to true.
-func StyleShow() Style {
+// Hidden is a prebuilt style with the `Hidden` property set to true.
+func Hidden() Style {
return Style{
- Show: true,
+ Hidden: true,
+ }
+}
+
+// Shown is a prebuilt style with the `Hidden` property set to false.
+// You can also think of this as the default.
+func Shown() Style {
+ return Style{
+ Hidden: false,
}
}
@@ -27,7 +34,7 @@ func StyleShow() Style {
func StyleTextDefaults() Style {
font, _ := GetDefaultFont()
return Style{
- Show: true,
+ Hidden: false,
Font: font,
FontColor: DefaultTextColor,
FontSize: DefaultTitleFontSize,
@@ -36,9 +43,11 @@ func StyleTextDefaults() Style {
// Style is a simple style set.
type Style struct {
- Show bool
+ Hidden bool
Padding Box
+ ClassName string
+
StrokeWidth float64
StrokeColor drawing.Color
StrokeDashArray []float64
@@ -64,14 +73,16 @@ type Style struct {
// IsZero returns if the object is set or not.
func (s Style) IsZero() bool {
- return s.StrokeColor.IsZero() &&
+ return !s.Hidden &&
+ s.StrokeColor.IsZero() &&
s.StrokeWidth == 0 &&
s.DotColor.IsZero() &&
s.DotWidth == 0 &&
s.FillColor.IsZero() &&
s.FontColor.IsZero() &&
s.FontSize == 0 &&
- s.Font == nil
+ s.Font == nil &&
+ s.ClassName == ""
}
// String returns a text representation of the style.
@@ -81,10 +92,16 @@ func (s Style) String() string {
}
var output []string
- if s.Show {
- output = []string{"\"show\": true"}
+ if s.Hidden {
+ output = []string{"\"hidden\": true"}
+ } else {
+ output = []string{"\"hidden\": false"}
+ }
+
+ if s.ClassName != "" {
+ output = append(output, fmt.Sprintf("\"class_name\": %s", s.ClassName))
} else {
- output = []string{"\"show\": false"}
+ output = append(output, "\"class_name\": null")
}
if !s.Padding.IsZero() {
@@ -155,6 +172,17 @@ func (s Style) String() string {
return "{" + strings.Join(output, ", ") + "}"
}
+// GetClassName returns the class name or a default.
+func (s Style) GetClassName(defaults ...string) string {
+ if s.ClassName == "" {
+ if len(defaults) > 0 {
+ return defaults[0]
+ }
+ return ""
+ }
+ return s.ClassName
+}
+
// GetStrokeColor returns the stroke color.
func (s Style) GetStrokeColor(defaults ...drawing.Color) drawing.Color {
if s.StrokeColor.IsZero() {
@@ -321,6 +349,7 @@ func (s Style) GetTextRotationDegrees(defaults ...float64) float64 {
// WriteToRenderer passes the style's options to a renderer.
func (s Style) WriteToRenderer(r Renderer) {
+ r.SetClassName(s.GetClassName())
r.SetStrokeColor(s.GetStrokeColor())
r.SetStrokeWidth(s.GetStrokeWidth())
r.SetStrokeDashArray(s.GetStrokeDashArray())
@@ -331,12 +360,13 @@ func (s Style) WriteToRenderer(r Renderer) {
r.ClearTextRotation()
if s.GetTextRotationDegrees() != 0 {
- r.SetTextRotation(util.Math.DegreesToRadians(s.GetTextRotationDegrees()))
+ r.SetTextRotation(DegreesToRadians(s.GetTextRotationDegrees()))
}
}
// WriteDrawingOptionsToRenderer passes just the drawing style options to a renderer.
func (s Style) WriteDrawingOptionsToRenderer(r Renderer) {
+ r.SetClassName(s.GetClassName())
r.SetStrokeColor(s.GetStrokeColor())
r.SetStrokeWidth(s.GetStrokeWidth())
r.SetStrokeDashArray(s.GetStrokeDashArray())
@@ -345,6 +375,7 @@ func (s Style) WriteDrawingOptionsToRenderer(r Renderer) {
// WriteTextOptionsToRenderer passes just the text style options to a renderer.
func (s Style) WriteTextOptionsToRenderer(r Renderer) {
+ r.SetClassName(s.GetClassName())
r.SetFont(s.GetFont())
r.SetFontColor(s.GetFontColor())
r.SetFontSize(s.GetFontSize())
@@ -352,6 +383,8 @@ func (s Style) WriteTextOptionsToRenderer(r Renderer) {
// InheritFrom coalesces two styles into a new style.
func (s Style) InheritFrom(defaults Style) (final Style) {
+ final.ClassName = s.GetClassName(defaults.ClassName)
+
final.StrokeColor = s.GetStrokeColor(defaults.StrokeColor)
final.StrokeWidth = s.GetStrokeWidth(defaults.StrokeWidth)
final.StrokeDashArray = s.GetStrokeDashArray(defaults.StrokeDashArray)
@@ -379,6 +412,7 @@ func (s Style) InheritFrom(defaults Style) (final Style) {
// GetStrokeOptions returns the stroke components.
func (s Style) GetStrokeOptions() Style {
return Style{
+ ClassName: s.ClassName,
StrokeDashArray: s.StrokeDashArray,
StrokeColor: s.StrokeColor,
StrokeWidth: s.StrokeWidth,
@@ -388,6 +422,7 @@ func (s Style) GetStrokeOptions() Style {
// GetFillOptions returns the fill components.
func (s Style) GetFillOptions() Style {
return Style{
+ ClassName: s.ClassName,
FillColor: s.FillColor,
}
}
@@ -395,6 +430,7 @@ func (s Style) GetFillOptions() Style {
// GetDotOptions returns the dot components.
func (s Style) GetDotOptions() Style {
return Style{
+ ClassName: s.ClassName,
StrokeDashArray: nil,
FillColor: s.DotColor,
StrokeColor: s.DotColor,
@@ -405,6 +441,7 @@ func (s Style) GetDotOptions() Style {
// GetFillAndStrokeOptions returns the fill and stroke components.
func (s Style) GetFillAndStrokeOptions() Style {
return Style{
+ ClassName: s.ClassName,
StrokeDashArray: s.StrokeDashArray,
FillColor: s.FillColor,
StrokeColor: s.StrokeColor,
@@ -415,6 +452,7 @@ func (s Style) GetFillAndStrokeOptions() Style {
// GetTextOptions returns just the text components of the style.
func (s Style) GetTextOptions() Style {
return Style{
+ ClassName: s.ClassName,
FontColor: s.FontColor,
FontSize: s.FontSize,
Font: s.Font,
diff --git a/vendor/github.com/wcharczuk/go-chart/text.go b/vendor/github.com/wcharczuk/go-chart/v2/text.go
similarity index 96%
rename from vendor/github.com/wcharczuk/go-chart/text.go
rename to vendor/github.com/wcharczuk/go-chart/v2/text.go
index a312c5b..0a9dfd0 100644
--- a/vendor/github.com/wcharczuk/go-chart/text.go
+++ b/vendor/github.com/wcharczuk/go-chart/v2/text.go
@@ -2,8 +2,6 @@ package chart
import (
"strings"
-
- util "github.com/wcharczuk/go-chart/util"
)
// TextHorizontalAlign is an enum for the horizontal alignment options.
@@ -48,7 +46,7 @@ const (
TextVerticalAlignBottom TextVerticalAlign = 2
// TextVerticalAlignMiddle aligns the text so that there is an equal amount of space above and below the top and bottom of the ligatures.
TextVerticalAlignMiddle TextVerticalAlign = 3
- // TextVerticalAlignMiddleBaseline aligns the text veritcally so that there is an equal number of pixels above and below the baseline of the string.
+ // TextVerticalAlignMiddleBaseline aligns the text vertically so that there is an equal number of pixels above and below the baseline of the string.
TextVerticalAlignMiddleBaseline TextVerticalAlign = 4
// TextVerticalAlignTop alignts the text so that the top of the ligatures are at y-pixel 0 in the container.
TextVerticalAlignTop TextVerticalAlign = 5
@@ -149,7 +147,7 @@ func (t text) MeasureLines(r Renderer, lines []string, style Style) Box {
var output Box
for index, line := range lines {
lineBox := r.MeasureText(line)
- output.Right = util.Math.MaxInt(lineBox.Right, output.Right)
+ output.Right = MaxInt(lineBox.Right, output.Right)
output.Bottom += lineBox.Height()
if index < len(lines)-1 {
output.Bottom += +style.GetTextLineSpacing()
diff --git a/vendor/github.com/wcharczuk/go-chart/tick.go b/vendor/github.com/wcharczuk/go-chart/v2/tick.go
similarity index 87%
rename from vendor/github.com/wcharczuk/go-chart/tick.go
rename to vendor/github.com/wcharczuk/go-chart/v2/tick.go
index 72ff9c5..1732c60 100644
--- a/vendor/github.com/wcharczuk/go-chart/tick.go
+++ b/vendor/github.com/wcharczuk/go-chart/v2/tick.go
@@ -4,8 +4,6 @@ import (
"fmt"
"math"
"strings"
-
- util "github.com/wcharczuk/go-chart/util"
)
// TicksProvider is a type that provides ticks.
@@ -85,15 +83,15 @@ func GenerateContinuousTicks(r Renderer, ra Range, isVertical bool, style Style,
rangeDelta := math.Abs(max - min)
tickStep := rangeDelta / float64(intermediateTickCount)
- roundTo := util.Math.GetRoundToForDelta(rangeDelta) / 10
- intermediateTickCount = util.Math.MinInt(intermediateTickCount, DefaultTickCountSanityCheck)
+ roundTo := GetRoundToForDelta(rangeDelta) / 10
+ intermediateTickCount = MinInt(intermediateTickCount, DefaultTickCountSanityCheck)
for x := 1; x < intermediateTickCount; x++ {
var tickValue float64
if ra.IsDescending() {
- tickValue = max - util.Math.RoundUp(tickStep*float64(x), roundTo)
+ tickValue = max - RoundUp(tickStep*float64(x), roundTo)
} else {
- tickValue = min + util.Math.RoundUp(tickStep*float64(x), roundTo)
+ tickValue = min + RoundUp(tickStep*float64(x), roundTo)
}
ticks = append(ticks, Tick{
Value: tickValue,
diff --git a/vendor/github.com/wcharczuk/go-chart/time_series.go b/vendor/github.com/wcharczuk/go-chart/v2/time_series.go
similarity index 73%
rename from vendor/github.com/wcharczuk/go-chart/time_series.go
rename to vendor/github.com/wcharczuk/go-chart/v2/time_series.go
index d2636a1..83ee905 100644
--- a/vendor/github.com/wcharczuk/go-chart/time_series.go
+++ b/vendor/github.com/wcharczuk/go-chart/v2/time_series.go
@@ -3,8 +3,14 @@ package chart
import (
"fmt"
"time"
+)
- util "github.com/wcharczuk/go-chart/util"
+// Interface Assertions.
+var (
+ _ Series = (*TimeSeries)(nil)
+ _ FirstValuesProvider = (*TimeSeries)(nil)
+ _ LastValuesProvider = (*TimeSeries)(nil)
+ _ ValueFormatterProvider = (*TimeSeries)(nil)
)
// TimeSeries is a line on a chart.
@@ -33,16 +39,23 @@ func (ts TimeSeries) Len() int {
return len(ts.XValues)
}
-// GetValues gets a value at a given index.
+// GetValues gets x, y values at a given index.
func (ts TimeSeries) GetValues(index int) (x, y float64) {
- x = util.Time.ToFloat64(ts.XValues[index])
+ x = TimeToFloat64(ts.XValues[index])
y = ts.YValues[index]
return
}
-// GetLastValues gets the last value.
+// GetFirstValues gets the first values.
+func (ts TimeSeries) GetFirstValues() (x, y float64) {
+ x = TimeToFloat64(ts.XValues[0])
+ y = ts.YValues[0]
+ return
+}
+
+// GetLastValues gets the last values.
func (ts TimeSeries) GetLastValues() (x, y float64) {
- x = util.Time.ToFloat64(ts.XValues[len(ts.XValues)-1])
+ x = TimeToFloat64(ts.XValues[len(ts.XValues)-1])
y = ts.YValues[len(ts.YValues)-1]
return
}
diff --git a/vendor/github.com/wcharczuk/go-chart/v2/times.go b/vendor/github.com/wcharczuk/go-chart/v2/times.go
new file mode 100644
index 0000000..95e2acd
--- /dev/null
+++ b/vendor/github.com/wcharczuk/go-chart/v2/times.go
@@ -0,0 +1,46 @@
+package chart
+
+import (
+ "sort"
+ "time"
+)
+
+// Assert types implement interfaces.
+var (
+ _ Sequence = (*Times)(nil)
+ _ sort.Interface = (*Times)(nil)
+)
+
+// Times are an array of times.
+// It wraps the array with methods that implement `seq.Provider`.
+type Times []time.Time
+
+// Array returns the times to an array.
+func (t Times) Array() []time.Time {
+ return []time.Time(t)
+}
+
+// Len returns the length of the array.
+func (t Times) Len() int {
+ return len(t)
+}
+
+// GetValue returns a value at an index as a time.
+func (t Times) GetValue(index int) float64 {
+ return ToFloat64(t[index])
+}
+
+// Swap implements sort.Interface.
+func (t Times) Swap(i, j int) {
+ t[i], t[j] = t[j], t[i]
+}
+
+// Less implements sort.Interface.
+func (t Times) Less(i, j int) bool {
+ return t[i].Before(t[j])
+}
+
+// ToFloat64 returns a float64 representation of a time.
+func ToFloat64(t time.Time) float64 {
+ return float64(t.UnixNano())
+}
diff --git a/vendor/github.com/wcharczuk/go-chart/v2/timeutil.go b/vendor/github.com/wcharczuk/go-chart/v2/timeutil.go
new file mode 100644
index 0000000..aa6b9e4
--- /dev/null
+++ b/vendor/github.com/wcharczuk/go-chart/v2/timeutil.go
@@ -0,0 +1,150 @@
+package chart
+
+import "time"
+
+// SecondsPerXYZ
+const (
+ SecondsPerHour = 60 * 60
+ SecondsPerDay = 60 * 60 * 24
+)
+
+// TimeMillis returns a duration as a float millis.
+func TimeMillis(d time.Duration) float64 {
+ return float64(d) / float64(time.Millisecond)
+}
+
+// DiffHours returns the difference in hours between two times.
+func DiffHours(t1, t2 time.Time) (hours int) {
+ t1n := t1.Unix()
+ t2n := t2.Unix()
+ var diff int64
+ if t1n > t2n {
+ diff = t1n - t2n
+ } else {
+ diff = t2n - t1n
+ }
+ return int(diff / (SecondsPerHour))
+}
+
+// TimeMin returns the minimum and maximum times in a given range.
+func TimeMin(times ...time.Time) (min time.Time) {
+ if len(times) == 0 {
+ return
+ }
+ min = times[0]
+ for index := 1; index < len(times); index++ {
+ if times[index].Before(min) {
+ min = times[index]
+ }
+
+ }
+ return
+}
+
+// TimeMax returns the minimum and maximum times in a given range.
+func TimeMax(times ...time.Time) (max time.Time) {
+ if len(times) == 0 {
+ return
+ }
+ max = times[0]
+
+ for index := 1; index < len(times); index++ {
+ if times[index].After(max) {
+ max = times[index]
+ }
+ }
+ return
+}
+
+// TimeMinMax returns the minimum and maximum times in a given range.
+func TimeMinMax(times ...time.Time) (min, max time.Time) {
+ if len(times) == 0 {
+ return
+ }
+ min = times[0]
+ max = times[0]
+
+ for index := 1; index < len(times); index++ {
+ if times[index].Before(min) {
+ min = times[index]
+ }
+ if times[index].After(max) {
+ max = times[index]
+ }
+ }
+ return
+}
+
+// TimeToFloat64 returns a float64 representation of a time.
+func TimeToFloat64(t time.Time) float64 {
+ return float64(t.UnixNano())
+}
+
+// TimeFromFloat64 returns a time from a float64.
+func TimeFromFloat64(tf float64) time.Time {
+ return time.Unix(0, int64(tf))
+}
+
+// TimeDescending sorts a given list of times ascending, or min to max.
+type TimeDescending []time.Time
+
+// Len implements sort.Sorter
+func (d TimeDescending) Len() int { return len(d) }
+
+// Swap implements sort.Sorter
+func (d TimeDescending) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
+
+// Less implements sort.Sorter
+func (d TimeDescending) Less(i, j int) bool { return d[i].After(d[j]) }
+
+// TimeAscending sorts a given list of times ascending, or min to max.
+type TimeAscending []time.Time
+
+// Len implements sort.Sorter
+func (a TimeAscending) Len() int { return len(a) }
+
+// Swap implements sort.Sorter
+func (a TimeAscending) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+
+// Less implements sort.Sorter
+func (a TimeAscending) Less(i, j int) bool { return a[i].Before(a[j]) }
+
+// Days generates a seq of timestamps by day, from -days to today.
+func Days(days int) []time.Time {
+ var values []time.Time
+ for day := days; day >= 0; day-- {
+ values = append(values, time.Now().AddDate(0, 0, -day))
+ }
+ return values
+}
+
+// Hours returns a sequence of times by the hour for a given number of hours
+// after a given start.
+func Hours(start time.Time, totalHours int) []time.Time {
+ times := make([]time.Time, totalHours)
+
+ last := start
+ for i := 0; i < totalHours; i++ {
+ times[i] = last
+ last = last.Add(time.Hour)
+ }
+
+ return times
+}
+
+// HoursFilled adds zero values for the data bounded by the start and end of the xdata array.
+func HoursFilled(xdata []time.Time, ydata []float64) ([]time.Time, []float64) {
+ start, end := TimeMinMax(xdata...)
+ totalHours := DiffHours(start, end)
+
+ finalTimes := Hours(start, totalHours+1)
+ finalValues := make([]float64, totalHours+1)
+
+ var hoursFromStart int
+ for i, xd := range xdata {
+ hoursFromStart = DiffHours(start, xd)
+ finalValues[hoursFromStart] = ydata[i]
+ }
+
+ return finalTimes, finalValues
+}
diff --git a/vendor/github.com/wcharczuk/go-chart/value.go b/vendor/github.com/wcharczuk/go-chart/v2/value.go
similarity index 85%
rename from vendor/github.com/wcharczuk/go-chart/value.go
rename to vendor/github.com/wcharczuk/go-chart/v2/value.go
index 75eedbb..783e304 100644
--- a/vendor/github.com/wcharczuk/go-chart/value.go
+++ b/vendor/github.com/wcharczuk/go-chart/v2/value.go
@@ -1,7 +1,5 @@
package chart
-import util "github.com/wcharczuk/go-chart/util"
-
// Value is a chart value.
type Value struct {
Style Style
@@ -23,7 +21,7 @@ func (vs Values) Values() []float64 {
// ValuesNormalized returns normalized values.
func (vs Values) ValuesNormalized() []float64 {
- return util.Math.Normalize(vs.Values()...)
+ return Normalize(vs.Values()...)
}
// Normalize returns the values normalized.
@@ -40,7 +38,7 @@ func (vs Values) Normalize() []Value {
output = append(output, Value{
Style: v.Style,
Label: v.Label,
- Value: util.Math.RoundDown(v.Value/total, 0.0001),
+ Value: RoundDown(v.Value/total, 0.0001),
})
}
}
diff --git a/vendor/github.com/wcharczuk/go-chart/seq/buffer.go b/vendor/github.com/wcharczuk/go-chart/v2/value_buffer.go
similarity index 79%
rename from vendor/github.com/wcharczuk/go-chart/seq/buffer.go
rename to vendor/github.com/wcharczuk/go-chart/v2/value_buffer.go
index be7c32e..d544bd3 100644
--- a/vendor/github.com/wcharczuk/go-chart/seq/buffer.go
+++ b/vendor/github.com/wcharczuk/go-chart/v2/value_buffer.go
@@ -1,10 +1,8 @@
-package seq
+package chart
import (
"fmt"
"strings"
-
- util "github.com/wcharczuk/go-chart/util"
)
const (
@@ -14,19 +12,15 @@ const (
bufferDefaultCapacity = 4
)
-var (
- emptyArray = make([]float64, 0)
-)
-
-// NewBuffer creates a new value buffer with an optional set of values.
-func NewBuffer(values ...float64) *Buffer {
+// NewValueBuffer creates a new value buffer with an optional set of values.
+func NewValueBuffer(values ...float64) *ValueBuffer {
var tail int
- array := make([]float64, util.Math.MaxInt(len(values), bufferDefaultCapacity))
+ array := make([]float64, MaxInt(len(values), bufferDefaultCapacity))
if len(values) > 0 {
copy(array, values)
tail = len(values)
}
- return &Buffer{
+ return &ValueBuffer{
array: array,
head: 0,
tail: tail,
@@ -34,9 +28,9 @@ func NewBuffer(values ...float64) *Buffer {
}
}
-// NewBufferWithCapacity creates a new ValueBuffer pre-allocated with the given capacity.
-func NewBufferWithCapacity(capacity int) *Buffer {
- return &Buffer{
+// NewValueBufferWithCapacity creates a new ValueBuffer pre-allocated with the given capacity.
+func NewValueBufferWithCapacity(capacity int) *ValueBuffer {
+ return &ValueBuffer{
array: make([]float64, capacity),
head: 0,
tail: 0,
@@ -44,11 +38,11 @@ func NewBufferWithCapacity(capacity int) *Buffer {
}
}
-// Buffer is a fifo datastructure that is backed by a pre-allocated array.
+// ValueBuffer is a fifo datastructure that is backed by a pre-allocated array.
// Instead of allocating a whole new node object for each element, array elements are re-used (which saves GC churn).
// Enqueue can be O(n), Dequeue is generally O(1).
// Buffer implements `seq.Provider`
-type Buffer struct {
+type ValueBuffer struct {
array []float64
head int
tail int
@@ -57,23 +51,23 @@ type Buffer struct {
// Len returns the length of the Buffer (as it is currently populated).
// Actual memory footprint may be different.
-func (b *Buffer) Len() int {
+func (b *ValueBuffer) Len() int {
return b.size
}
// GetValue implements seq provider.
-func (b *Buffer) GetValue(index int) float64 {
+func (b *ValueBuffer) GetValue(index int) float64 {
effectiveIndex := (b.head + index) % len(b.array)
return b.array[effectiveIndex]
}
// Capacity returns the total size of the Buffer, including empty elements.
-func (b *Buffer) Capacity() int {
+func (b *ValueBuffer) Capacity() int {
return len(b.array)
}
// SetCapacity sets the capacity of the Buffer.
-func (b *Buffer) SetCapacity(capacity int) {
+func (b *ValueBuffer) SetCapacity(capacity int) {
newArray := make([]float64, capacity)
if b.size > 0 {
if b.head < b.tail {
@@ -93,7 +87,7 @@ func (b *Buffer) SetCapacity(capacity int) {
}
// Clear removes all objects from the Buffer.
-func (b *Buffer) Clear() {
+func (b *ValueBuffer) Clear() {
b.array = make([]float64, bufferDefaultCapacity)
b.head = 0
b.tail = 0
@@ -101,7 +95,7 @@ func (b *Buffer) Clear() {
}
// Enqueue adds an element to the "back" of the Buffer.
-func (b *Buffer) Enqueue(value float64) {
+func (b *ValueBuffer) Enqueue(value float64) {
if b.size == len(b.array) {
newCapacity := int(len(b.array) * int(bufferGrowFactor/100))
if newCapacity < (len(b.array) + bufferMinimumGrow) {
@@ -116,7 +110,7 @@ func (b *Buffer) Enqueue(value float64) {
}
// Dequeue removes the first element from the RingBuffer.
-func (b *Buffer) Dequeue() float64 {
+func (b *ValueBuffer) Dequeue() float64 {
if b.size == 0 {
return 0
}
@@ -128,7 +122,7 @@ func (b *Buffer) Dequeue() float64 {
}
// Peek returns but does not remove the first element.
-func (b *Buffer) Peek() float64 {
+func (b *ValueBuffer) Peek() float64 {
if b.size == 0 {
return 0
}
@@ -136,7 +130,7 @@ func (b *Buffer) Peek() float64 {
}
// PeekBack returns but does not remove the last element.
-func (b *Buffer) PeekBack() float64 {
+func (b *ValueBuffer) PeekBack() float64 {
if b.size == 0 {
return 0
}
@@ -147,7 +141,7 @@ func (b *Buffer) PeekBack() float64 {
}
// TrimExcess resizes the capacity of the buffer to better fit the contents.
-func (b *Buffer) TrimExcess() {
+func (b *ValueBuffer) TrimExcess() {
threshold := float64(len(b.array)) * 0.9
if b.size < int(threshold) {
b.SetCapacity(b.size)
@@ -155,7 +149,7 @@ func (b *Buffer) TrimExcess() {
}
// Array returns the ring buffer, in order, as an array.
-func (b *Buffer) Array() Array {
+func (b *ValueBuffer) Array() Array {
newArray := make([]float64, b.size)
if b.size == 0 {
@@ -173,7 +167,7 @@ func (b *Buffer) Array() Array {
}
// Each calls the consumer for each element in the buffer.
-func (b *Buffer) Each(mapfn func(int, float64)) {
+func (b *ValueBuffer) Each(mapfn func(int, float64)) {
if b.size == 0 {
return
}
@@ -197,7 +191,7 @@ func (b *Buffer) Each(mapfn func(int, float64)) {
}
// String returns a string representation for value buffers.
-func (b *Buffer) String() string {
+func (b *ValueBuffer) String() string {
var values []string
for _, elem := range b.Array() {
values = append(values, fmt.Sprintf("%v", elem))
diff --git a/vendor/github.com/wcharczuk/go-chart/value_formatter.go b/vendor/github.com/wcharczuk/go-chart/v2/value_formatter.go
similarity index 80%
rename from vendor/github.com/wcharczuk/go-chart/value_formatter.go
rename to vendor/github.com/wcharczuk/go-chart/v2/value_formatter.go
index 1c264c2..468f3bd 100644
--- a/vendor/github.com/wcharczuk/go-chart/value_formatter.go
+++ b/vendor/github.com/wcharczuk/go-chart/v2/value_formatter.go
@@ -2,6 +2,7 @@ package chart
import (
"fmt"
+ "strconv"
"time"
)
@@ -49,6 +50,22 @@ func formatTime(v interface{}, dateFormat string) string {
return ""
}
+// IntValueFormatter is a ValueFormatter for float64.
+func IntValueFormatter(v interface{}) string {
+ switch v.(type) {
+ case int:
+ return strconv.Itoa(v.(int))
+ case int64:
+ return strconv.FormatInt(v.(int64), 10)
+ case float32:
+ return strconv.FormatInt(int64(v.(float32)), 10)
+ case float64:
+ return strconv.FormatInt(int64(v.(float64)), 10)
+ default:
+ return ""
+ }
+}
+
// FloatValueFormatter is a ValueFormatter for float64.
func FloatValueFormatter(v interface{}) string {
return FloatValueFormatterWithFormat(v, DefaultFloatFormat)
@@ -65,17 +82,24 @@ func PercentValueFormatter(v interface{}) string {
// FloatValueFormatterWithFormat is a ValueFormatter for float64 with a given format.
func FloatValueFormatterWithFormat(v interface{}, floatFormat string) string {
- if typed, isTyped := v.(float64); isTyped {
- return fmt.Sprintf(floatFormat, typed)
- }
- if typed, isTyped := v.(float32); isTyped {
- return fmt.Sprintf(floatFormat, typed)
- }
if typed, isTyped := v.(int); isTyped {
return fmt.Sprintf(floatFormat, float64(typed))
}
if typed, isTyped := v.(int64); isTyped {
return fmt.Sprintf(floatFormat, float64(typed))
}
+ if typed, isTyped := v.(float32); isTyped {
+ return fmt.Sprintf(floatFormat, typed)
+ }
+ if typed, isTyped := v.(float64); isTyped {
+ return fmt.Sprintf(floatFormat, typed)
+ }
return ""
}
+
+// KValueFormatter is a formatter for K values.
+func KValueFormatter(k float64, vf ValueFormatter) ValueFormatter {
+ return func(v interface{}) string {
+ return fmt.Sprintf("%0.0fσ %s", k, vf(v))
+ }
+}
diff --git a/vendor/github.com/wcharczuk/go-chart/value_formatter_provider.go b/vendor/github.com/wcharczuk/go-chart/v2/value_formatter_provider.go
similarity index 100%
rename from vendor/github.com/wcharczuk/go-chart/value_formatter_provider.go
rename to vendor/github.com/wcharczuk/go-chart/v2/value_formatter_provider.go
diff --git a/vendor/github.com/wcharczuk/go-chart/value_provider.go b/vendor/github.com/wcharczuk/go-chart/v2/value_provider.go
similarity index 86%
rename from vendor/github.com/wcharczuk/go-chart/value_provider.go
rename to vendor/github.com/wcharczuk/go-chart/v2/value_provider.go
index e93c30d..be985eb 100644
--- a/vendor/github.com/wcharczuk/go-chart/value_provider.go
+++ b/vendor/github.com/wcharczuk/go-chart/v2/value_provider.go
@@ -1,6 +1,6 @@
package chart
-import "github.com/wcharczuk/go-chart/drawing"
+import "github.com/wcharczuk/go-chart/v2/drawing"
// ValuesProvider is a type that produces values.
type ValuesProvider interface {
@@ -14,6 +14,11 @@ type BoundedValuesProvider interface {
GetBoundedValues(index int) (x, y1, y2 float64)
}
+// FirstValuesProvider is a special type of value provider that can return it's (potentially computed) first value.
+type FirstValuesProvider interface {
+ GetFirstValues() (x, y float64)
+}
+
// LastValuesProvider is a special type of value provider that can return it's (potentially computed) last value.
type LastValuesProvider interface {
GetLastValues() (x, y float64)
diff --git a/vendor/github.com/wcharczuk/go-chart/vector_renderer.go b/vendor/github.com/wcharczuk/go-chart/v2/vector_renderer.go
similarity index 74%
rename from vendor/github.com/wcharczuk/go-chart/vector_renderer.go
rename to vendor/github.com/wcharczuk/go-chart/v2/vector_renderer.go
index 6f9b6f4..eb2fc83 100644
--- a/vendor/github.com/wcharczuk/go-chart/vector_renderer.go
+++ b/vendor/github.com/wcharczuk/go-chart/v2/vector_renderer.go
@@ -10,8 +10,7 @@ import (
"golang.org/x/image/font"
"github.com/golang/freetype/truetype"
- "github.com/wcharczuk/go-chart/drawing"
- "github.com/wcharczuk/go-chart/util"
+ "github.com/wcharczuk/go-chart/v2/drawing"
)
// SVG returns a new png/raster renderer.
@@ -28,6 +27,25 @@ func SVG(width, height int) (Renderer, error) {
}, nil
}
+// SVGWithCSS returns a new png/raster renderer with attached custom CSS
+// The optional nonce argument sets a CSP nonce.
+func SVGWithCSS(css string, nonce string) func(width, height int) (Renderer, error) {
+ return func(width, height int) (Renderer, error) {
+ buffer := bytes.NewBuffer([]byte{})
+ canvas := newCanvas(buffer)
+ canvas.css = css
+ canvas.nonce = nonce
+ canvas.Start(width, height)
+ return &vectorRenderer{
+ b: buffer,
+ c: canvas,
+ s: &Style{},
+ p: []string{},
+ dpi: DefaultDPI,
+ }, nil
+ }
+}
+
// vectorRenderer renders chart commands to a bitmap.
type vectorRenderer struct {
dpi float64
@@ -54,6 +72,11 @@ func (vr *vectorRenderer) SetDPI(dpi float64) {
vr.c.dpi = dpi
}
+// SetClassName implements the interface method.
+func (vr *vectorRenderer) SetClassName(classname string) {
+ vr.s.ClassName = classname
+}
+
// SetStrokeColor implements the interface method.
func (vr *vectorRenderer) SetStrokeColor(c drawing.Color) {
vr.s.StrokeColor = c
@@ -90,8 +113,8 @@ func (vr *vectorRenderer) QuadCurveTo(cx, cy, x, y int) {
}
func (vr *vectorRenderer) ArcTo(cx, cy int, rx, ry, startAngle, delta float64) {
- startAngle = util.Math.RadianAdd(startAngle, _pi2)
- endAngle := util.Math.RadianAdd(startAngle, delta)
+ startAngle = RadianAdd(startAngle, _pi2)
+ endAngle := RadianAdd(startAngle, delta)
startx := cx + int(rx*math.Sin(startAngle))
starty := cy - int(ry*math.Cos(startAngle))
@@ -105,7 +128,7 @@ func (vr *vectorRenderer) ArcTo(cx, cy int, rx, ry, startAngle, delta float64) {
endx := cx + int(rx*math.Sin(endAngle))
endy := cy - int(ry*math.Cos(endAngle))
- dd := util.Math.RadiansToDegrees(delta)
+ dd := RadiansToDegrees(delta)
largeArcFlag := 0
if delta > _pi {
@@ -182,7 +205,7 @@ func (vr *vectorRenderer) MeasureText(body string) (box Box) {
if vr.c.textTheta == nil {
return
}
- box = box.Corners().Rotate(util.Math.RadiansToDegrees(*vr.c.textTheta)).Box()
+ box = box.Corners().Rotate(RadiansToDegrees(*vr.c.textTheta)).Box()
}
return
}
@@ -217,12 +240,23 @@ type canvas struct {
textTheta *float64
width int
height int
+ css string
+ nonce string
}
func (c *canvas) Start(width, height int) {
c.width = width
c.height = height
c.w.Write([]byte(fmt.Sprintf(`