diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index bdae4bdc..5013ac48 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -8,6 +8,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + - name: cgo-dependencies + run: sudo apt install pkg-config libgles2-mesa-dev libxkbcommon-dev libxkbcommon-x11-dev - name: golangci-lint uses: golangci/golangci-lint-action@v1 with: diff --git a/.travis.yml b/.travis.yml index 2edf729e..52924353 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -sudo: false +sudo: true language: go go_import_path: gonum.org/v1/plot @@ -29,6 +29,9 @@ before_install: - go get golang.org/x/tools/cmd/cover - go get github.com/mattn/goveralls +before_script: + - sudo apt-get install -qq pkg-config libgles2-mesa-dev libxkbcommon-dev libxkbcommon-x11-dev + # Get deps, build, test, and ensure the code is gofmt'ed. # If we are building as gonum, then we have access to the coveralls api key, so we can run coverage as well. script: diff --git a/go.mod b/go.mod index de4bd9e8..e48d1eb1 100644 --- a/go.mod +++ b/go.mod @@ -3,13 +3,14 @@ module gonum.org/v1/plot go 1.13 require ( + gioui.org v0.0.0-20200618124658-602d54dc5ef7 github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af github.com/fogleman/gg v1.3.0 github.com/go-latex/latex v0.0.0-20200518072620-0806b477ea35 github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 github.com/jung-kurt/gofpdf v1.16.2 - golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495 - golang.org/x/image v0.0.0-20200430140353-33d19683fad8 + golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3 + golang.org/x/image v0.0.0-20200618115811-c13761719519 gonum.org/v1/gonum v0.7.0 rsc.io/pdf v0.1.1 ) diff --git a/go.sum b/go.sum index 5fa2d608..18ce25cc 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,6 @@ +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +gioui.org v0.0.0-20200618124658-602d54dc5ef7 h1:PH02mpvkp5MNZcqi/wWY+YJTG5kvvuyc6kJ/efTGfXE= +gioui.org v0.0.0-20200618124658-602d54dc5ef7/go.mod h1:jiUwifN9cRl/zmco43aAqh0aV+s9GbhG13KcD+gEpkU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af h1:wVe6/Ea46ZMeNkQjjBW6xcqyQA/j5e0D6GytH95g0gQ= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= @@ -9,6 +12,7 @@ github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/go-fonts/dejavu v0.1.0 h1:JSajPXURYqpr+Cu8U9bt8K+XcACIHWqWrvWCKyeFmVQ= github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-latex/latex v0.0.0-20200518072620-0806b477ea35 h1:uroDDLmuCK5Pz5J/Ef5vCL6F0sJmAtZFTm0/cF027F4= github.com/go-latex/latex v0.0.0-20200518072620-0806b477ea35/go.mod h1:PNI+CcWytn/2Z/9f1SGOOYn0eILruVyp0v2/iAs8asQ= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= @@ -24,25 +28,37 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495 h1:I6A9Ag9FpEKOjcKrRNjQkPHawoXIhKyTGfvvjFAiiAk= -golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3 h1:n9HxLrNxWWtEb1cA950nuEEj3QnKbtsCJ6KjcgisNUs= +golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067 h1:KYGJGHOQy8oSi1fDlSpcZF0+juKwk/hEMv5SiwHogR0= 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-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20200430140353-33d19683fad8 h1:6WW6V3x1P/jokJBpRQYUJnMHRP6isStQwCozxnU7XQw= golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/image v0.0.0-20200618115811-c13761719519 h1:1e2ufUJNM3lCHEY5jIgac/7UTjd6cgJNdatjPdFWf34= +golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9 h1:1/DFK4b7JH8DmkqhUk48onnSfrPzImPoVxuomtbT2nk= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.7.0 h1:Hdks0L0hgznZLG9nzXb8vZ0rRvqNvAcgAp84y7Mwkgw= gonum.org/v1/gonum v0.7.0/go.mod h1:L02bwd0sqlsvRv41G7wGWFCsVNZFv/k1xzGIxeANHGM= diff --git a/vg/vggio/example_vg_test.go b/vg/vggio/example_vg_test.go new file mode 100644 index 00000000..865d54ce --- /dev/null +++ b/vg/vggio/example_vg_test.go @@ -0,0 +1,74 @@ +// Copyright ©2020 The Gonum Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package vggio_test + +import ( + "log" + "os" + "time" + + "gioui.org/app" + "gioui.org/io/key" + "gioui.org/io/system" + "gioui.org/unit" + + "gonum.org/v1/plot" + "gonum.org/v1/plot/vg" + "gonum.org/v1/plot/vg/draw" + "gonum.org/v1/plot/vg/vggio" +) + +func ExampleCanvas() { + const ( + w = 20 * vg.Centimeter + h = 15 * vg.Centimeter + dpi = 96 + ) + go func(w, h vg.Length) { + win := app.NewWindow( + app.Title("Gonum"), + app.Size( + unit.Px(float32(w.Dots(dpi))), + unit.Px(float32(h.Dots(dpi))), + ), + ) + defer win.Close() + + done := time.NewTimer(2 * time.Second) + defer done.Stop() + + for { + select { + case e := <-win.Events(): + switch e := e.(type) { + case system.FrameEvent: + p, err := plot.New() + if err != nil { + log.Fatalf("could not create plot: %+v", err) + } + p.Title.Text = "My title" + p.X.Label.Text = "X" + p.Y.Label.Text = "Y" + + cnv := vggio.New(e, w, h, vggio.UseDPI(dpi)) + p.Draw(draw.New(cnv)) + cnv.Paint(e) + + case key.Event: + switch e.Name { + case "Q", key.NameEscape: + os.Exit(0) + } + } + case <-done.C: + os.Exit(0) + } + } + }(w, h) + + app.Main() + + // Output: +} diff --git a/vg/vggio/vg.go b/vg/vggio/vg.go new file mode 100644 index 00000000..04dfc0ff --- /dev/null +++ b/vg/vggio/vg.go @@ -0,0 +1,104 @@ +// Copyright ©2020 The Gonum Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package vggio provides a vg.Canvas implementation backed by Gioui. +package vggio // import "gonum.org/v1/plot/vg/vggio" + +import ( + "image/color" + + "gioui.org/f32" + "gioui.org/io/system" + "gioui.org/layout" + "gioui.org/op" + "gioui.org/op/paint" + + "gonum.org/v1/plot/vg" + "gonum.org/v1/plot/vg/vgimg" +) + +// Canvas implements the vg.Canvas interface, +// drawing to an image.Image using vgimg and painting that image +// into a Gioui context. +type Canvas struct { + *vgimg.Canvas + gtx layout.Context +} + +// DefaultDPI is the default dot resolution for image +// drawing in dots per inch. +const DefaultDPI = 96 + +// New returns a new image canvas with the provided dimensions and options. +// The currently accepted options are UseDPI and UseBackgroundColor. +// If the resolution or background color are not specified, defaults are used. +func New(e system.FrameEvent, w, h vg.Length, opts ...option) *Canvas { + cfg := &config{ + dpi: DefaultDPI, + bkg: color.White, + } + for _, opt := range opts { + opt(cfg) + } + c := &Canvas{ + gtx: layout.NewContext(new(op.Ops), e), + Canvas: vgimg.NewWith( + vgimg.UseDPI(cfg.dpi), + vgimg.UseWH(w, h), + vgimg.UseBackgroundColor(cfg.bkg), + ), + } + return c +} + +type config struct { + dpi int + bkg color.Color +} + +type option func(*config) + +// UseDPI sets the dots per inch of a canvas. It should only be +// used as an option argument when initializing a new canvas. +func UseDPI(dpi int) option { + if dpi <= 0 { + panic("DPI must be > 0.") + } + return func(c *config) { + c.dpi = dpi + } +} + +// UseBackgroundColor specifies the image background color. +// Without UseBackgroundColor, the default color is white. +func UseBackgroundColor(c color.Color) option { + return func(cfg *config) { + cfg.bkg = c + } +} + +func (c *Canvas) pt32(p vg.Point) f32.Point { + _, h := c.Size() + dpi := c.DPI() + return f32.Point{ + X: float32(p.X.Dots(dpi)), + Y: float32(h.Dots(dpi) - p.Y.Dots(dpi)), + } +} + +// Paint paints the canvas' content on the screen. +func (c *Canvas) Paint(e system.FrameEvent) { + w, h := c.Size() + box := vg.Rectangle{Max: vg.Point{X: w, Y: h}} + img := c.Canvas.Image() + ops := c.gtx.Ops + min := c.pt32(box.Min) + max := c.pt32(box.Max) + r32 := f32.Rect(min.X, min.Y, max.X, max.Y) + + paint.NewImageOp(img).Add(ops) + paint.PaintOp{Rect: r32}.Add(ops) + + e.Frame(ops) +}