├── example
├── text
│ ├── .gitignore
│ ├── AndroidManifest.xml
│ └── main.go
└── boxes
│ ├── AndroidManifest.xml
│ └── main.go
├── text
├── .gitignore
├── genout.go
├── gen.go
└── texcoords.go
├── assets
├── assets.go
├── gen.go
├── environment-vert.glsl
├── environment-frag.glsl
└── glsl.go
├── .gitignore
├── icon
├── makeicons.hbs
├── makeicons.go
└── icon_mdpi.go
├── go.mod
├── debug.go
├── atlas
├── atlas_test.go
└── atlas.go
├── LICENSE.bsd
├── size.go
├── anim.go
├── adaptive.go
├── README.md
├── layout.go
├── glutil
├── glutil.go
└── gl.go
├── material.go
├── color.go
└── environment.go
/example/text/.gitignore:
--------------------------------------------------------------------------------
1 | assets/
2 |
--------------------------------------------------------------------------------
/text/.gitignore:
--------------------------------------------------------------------------------
1 | *.png
2 | *.ttf
3 |
--------------------------------------------------------------------------------
/assets/assets.go:
--------------------------------------------------------------------------------
1 | //go:generate go run gen.go
2 |
3 | package assets
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *~
2 | \#*\#
3 | .\#*
4 | *.prof
5 | *.test
6 |
7 | *.png
8 | *.apk
9 |
--------------------------------------------------------------------------------
/icon/makeicons.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{#each layouts}}
3 | {{#each sprites}}
4 |
5 | {{../classname}}
6 | {{url}}
7 |
8 | {{/each}}
9 |
10 | {{#each layout.items}}
11 |
12 | {{../classname}}
13 | {{meta.name}}
14 | {{x}}
15 | {{y}}
16 |
17 | {{/each}}
18 | {{/each}}
19 |
20 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module dasa.cc/material
2 |
3 | go 1.14
4 |
5 | require (
6 | dasa.cc/signal v0.0.0-20221023170706-f88a600dd4cc
7 | dasa.cc/simplex v0.0.0-20180617055632-ae0aeef7c530
8 | dasa.cc/snd v0.0.0-20200301082333-54fd236f2e18
9 | dasa.cc/x v0.0.0-20221023141954-69d42d7509f2
10 | golang.org/x/image v0.1.0
11 | golang.org/x/mobile v0.0.0-20221020085226-b36e6246172e
12 | gopkg.in/fsnotify.v1 v1.4.7
13 | )
14 |
--------------------------------------------------------------------------------
/text/genout.go:
--------------------------------------------------------------------------------
1 | // +build ignore
2 |
3 | package main
4 |
5 | import (
6 | "fmt"
7 | "io/ioutil"
8 | "log"
9 | "strconv"
10 | )
11 |
12 | const tmpl = `// File is automatically generated by genout.go. DO NOT EDIT.
13 |
14 | package text
15 |
16 | var Texture = []byte(%s)`
17 |
18 | func main() {
19 | b, err := ioutil.ReadFile("out.png")
20 | if err != nil {
21 | log.Fatal(err)
22 | }
23 | if err := ioutil.WriteFile("out.go", []byte(fmt.Sprintf(tmpl, strconv.Quote(string(b)))), 0644); err != nil {
24 | log.Fatal(err)
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/assets/gen.go:
--------------------------------------------------------------------------------
1 | // +build ignore
2 |
3 | package main
4 |
5 | import (
6 | "bytes"
7 | "fmt"
8 | "io/ioutil"
9 | "log"
10 | )
11 |
12 | func mustreadfile(name string) []byte {
13 | b, err := ioutil.ReadFile(name)
14 | if err != nil {
15 | log.Fatal(err)
16 | }
17 | return b
18 | }
19 |
20 | func main() {
21 | vert := mustreadfile("environment-vert.glsl")
22 | frag := mustreadfile("environment-frag.glsl")
23 |
24 | buf := new(bytes.Buffer)
25 | buf.WriteString("// generated by gen.go; DO NOT EDIT\npackage assets\n\n")
26 | fmt.Fprintf(buf, "var VertexShader = `%s`\n\n", string(vert))
27 | fmt.Fprintf(buf, "var FragmentShader = `%s`\n", string(frag))
28 | if err := ioutil.WriteFile("glsl.go", buf.Bytes(), 0644); err != nil {
29 | log.Fatal(err)
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/example/text/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
11 |
12 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/example/boxes/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
11 |
12 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/debug.go:
--------------------------------------------------------------------------------
1 | package material
2 |
3 | import (
4 | "log"
5 | "path/filepath"
6 | "strings"
7 |
8 | "gopkg.in/fsnotify.v1"
9 | )
10 |
11 | func watchShaders() (chan string, chan bool) {
12 | watcher, err := fsnotify.NewWatcher()
13 | if err != nil {
14 | log.Fatal(err)
15 | }
16 |
17 | event := make(chan string)
18 | quit := make(chan bool)
19 |
20 | // Process events
21 | go func() {
22 | for {
23 | select {
24 | case ev := <-watcher.Events:
25 | log.Println(ev)
26 | // expects filenames such as triangle-vert.glsl or triangle-frag.glsl
27 | if filepath.Ext(ev.Name) == ".glsl" && ev.Op == fsnotify.Write {
28 | b := filepath.Base(ev.Name)
29 | i := strings.LastIndex(b, "-")
30 | b = b[:i]
31 | event <- b
32 | }
33 | case err := <-watcher.Errors:
34 | log.Println("watch error:", err)
35 | case <-quit:
36 | watcher.Close()
37 | return
38 | }
39 | }
40 | }()
41 |
42 | err = watcher.Add("./assets")
43 | if err != nil {
44 | log.Fatal("Failed to watch folder.", err)
45 | }
46 |
47 | return event, quit
48 | }
49 |
--------------------------------------------------------------------------------
/assets/environment-vert.glsl:
--------------------------------------------------------------------------------
1 | #version 100
2 |
3 | attribute vec4 vertex;
4 | attribute vec4 color;
5 |
6 | // sample with xy, offset at zw
7 | attribute vec4 texcoord;
8 |
9 | // xy is relative position of originating touch event
10 | // z is state of touch event; begin (0), move (1), end (2)
11 | // w is timing information
12 | attribute vec4 touch;
13 |
14 | // v0, v1 == 1 && v2, v3 == 0, interpolated in fragment shader
15 | // to determine location since all materials are drawn as single
16 | // mesh.
17 | attribute vec4 dist;
18 |
19 | // uniform mat4 world;
20 | uniform mat4 view;
21 | uniform mat4 proj;
22 |
23 | varying vec4 vcolor;
24 | varying vec4 vtexcoord;
25 | varying vec4 vdist;
26 | varying vec4 vvertex;
27 | varying vec4 vtouch;
28 |
29 | void main() {
30 | // TODO review if fragment shader *really* needs access to z coord
31 | // of shadow's material.
32 | vec4 vert = vec4(vertex.xyz, 1.0);
33 | if (vert.z < 0.0) {
34 | vert.z = 0.0;
35 | }
36 | //gl_Position = vert * view * proj;
37 | gl_Position = proj * view * vert;
38 | vcolor = color;
39 | vtexcoord = texcoord;
40 | vvertex = vertex;
41 | vdist = dist;
42 | vtouch = touch;
43 | }
44 |
--------------------------------------------------------------------------------
/atlas/atlas_test.go:
--------------------------------------------------------------------------------
1 | package atlas
2 |
3 | import (
4 | "image"
5 | "image/color"
6 | "image/draw"
7 | "math/rand"
8 | "testing"
9 | "time"
10 | )
11 |
12 | func TestAdd(t *testing.T) {
13 | rand.Seed(time.Now().UnixNano())
14 | atl := New(512, 512)
15 |
16 | const iter = 1000
17 | var errc int
18 | for i := 0; i < iter; i++ {
19 | m := image.NewNRGBA(image.Rect(0, 0, rand.Intn(50)+10, rand.Intn(50)+10))
20 | r, g, b := uint8(rand.Intn(200))+55, uint8(rand.Intn(200))+55, uint8(rand.Intn(200))+55
21 | draw.Draw(m, m.Bounds(), image.NewUniform(color.NRGBA{R: r, G: g, B: b, A: 120}), image.ZP, draw.Src)
22 |
23 | if _, err := atl.Add(m); err != nil {
24 | errc++
25 | }
26 | }
27 |
28 | t.Logf("inserted %v of %v", iter-errc, iter)
29 | if err := atl.writeFile("out.png"); err != nil {
30 | t.Error(err)
31 | }
32 | }
33 |
34 | func BenchmarkAdd(b *testing.B) {
35 | b.ReportAllocs()
36 | atl := New(512, 512)
37 | for n := 0; n < b.N; n++ {
38 | src := image.NewNRGBA(image.Rect(0, 0, rand.Intn(50)+10, rand.Intn(50)+10))
39 | r, g, b := uint8(rand.Intn(200))+55, uint8(rand.Intn(200))+55, uint8(rand.Intn(200))+55
40 | draw.Draw(src, src.Bounds(), image.NewUniform(color.NRGBA{R: r, G: g, B: b, A: 120}), image.ZP, draw.Src)
41 | atl.Add(src)
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/LICENSE.bsd:
--------------------------------------------------------------------------------
1 | Copyright (c) 2015, Daniel Skinner
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are met:
6 |
7 | 1. Redistributions of source code must retain the above copyright notice, this
8 | list of conditions and the following disclaimer.
9 | 2. Redistributions in binary form must reproduce the above copyright notice,
10 | this list of conditions and the following disclaimer in the documentation
11 | and/or other materials provided with the distribution.
12 |
13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
17 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 |
--------------------------------------------------------------------------------
/size.go:
--------------------------------------------------------------------------------
1 | package material
2 |
3 | import (
4 | "golang.org/x/mobile/event/size"
5 | "golang.org/x/mobile/exp/f32"
6 | )
7 |
8 | var windowSize size.Event
9 |
10 | // Uton converts unit to norm.
11 | func Uton(u float32) float32 { return 2*u - 1 }
12 |
13 | // Ntou converts norm to unit.
14 | func Ntou(n float32) float32 { return (n + 1) / 2 }
15 |
16 | // Mtoa converts mat4 to affine.
17 | // TODO this only exists since I'm doing 2D atm
18 | // and mat4 doesn't have inverse method.
19 | func Mtoa(m f32.Mat4) (a f32.Affine) {
20 | a[0][0] = m[0][0]
21 | a[0][1] = m[0][1]
22 | a[0][2] = m[0][3]
23 | a[1][0] = m[1][0]
24 | a[1][1] = m[1][1]
25 | a[1][2] = m[1][3]
26 | return
27 | }
28 |
29 | func ScreenToUnit(x, y float32) (float32, float32) {
30 | // TODO determine y direction, don't assume
31 | return x / float32(windowSize.WidthPx), 1 - (y / float32(windowSize.HeightPx))
32 | }
33 |
34 | func ScreenToNorm(x, y float32) (float32, float32) {
35 | x, y = ScreenToUnit(x, y)
36 | return Uton(x), Uton(y)
37 | }
38 |
39 | func ScreenToWorld(x, y, z float32, view, proj f32.Mat4) (float32, float32) {
40 | x, y = ScreenToNorm(x, y)
41 | return NormToWorld(x, y, z, view, proj)
42 | }
43 |
44 | func UnitToWorld(x, y, z float32, view, proj f32.Mat4) (float32, float32) {
45 | wx, wy := NormToWorld(Uton(x), Uton(y), Uton(z), view, proj)
46 | return 1 + wx, -wy
47 | }
48 |
49 | func NormToWorld(x, y, z float32, view, proj f32.Mat4) (float32, float32) {
50 | nv := f32.Vec3{x, y, z}
51 |
52 | unproj := Mtoa(proj)
53 | unproj.Inverse(&unproj)
54 | unview := Mtoa(view)
55 | unview.Inverse(&unview)
56 |
57 | nv[0], nv[1] = nv.Dot(&unproj[0]), nv.Dot(&unproj[1])
58 | nv[0], nv[1] = nv.Dot(&unview[0]), nv.Dot(&unview[1])
59 |
60 | return nv[0], nv[1]
61 | }
62 |
63 | func NormToView(x, y, z float32, proj f32.Mat4) (float32, float32) {
64 | nv := f32.Vec3{x, y, z}
65 | unproj := Mtoa(proj)
66 | unproj.Inverse(&unproj)
67 | nv[0], nv[1] = nv.Dot(&unproj[0]), nv.Dot(&unproj[1])
68 | return nv[0], nv[1]
69 | }
70 |
--------------------------------------------------------------------------------
/anim.go:
--------------------------------------------------------------------------------
1 | package material
2 |
3 | import (
4 | "time"
5 |
6 | "dasa.cc/signal"
7 | "golang.org/x/mobile/exp/f32"
8 | )
9 |
10 | var (
11 | ExpSig, LinSig signal.Discrete
12 | )
13 |
14 | func LinearDrive() signal.Discrete {
15 | sig := make(signal.Discrete, 1024)
16 | sig.Sample(func(t float64) float64 { return t }, 1./1024, 0)
17 | return sig
18 | }
19 |
20 | func init() {
21 | ExpSig = signal.ExpDecay()
22 | ExpSig.UnitInverse()
23 | LinSig = LinearDrive()
24 | }
25 |
26 | type Interpolator struct {
27 | Sig signal.Discrete
28 | Dur time.Duration
29 | Loop bool
30 | }
31 |
32 | type Animation struct {
33 | Sig signal.Discrete
34 | Dur time.Duration
35 | Loop bool
36 | Start func()
37 | Interp func(dt float32)
38 | End func()
39 | }
40 |
41 | func (anim Animation) Do() (quit chan struct{}) {
42 | quit = make(chan struct{}, 1)
43 | go func() {
44 | ticker := time.NewTicker(16 * time.Millisecond)
45 | start := time.Now()
46 | if anim.Start != nil {
47 | anim.Start()
48 | }
49 | for {
50 | select {
51 | case <-quit:
52 | ticker.Stop()
53 | if anim.End != nil {
54 | anim.End()
55 | }
56 | return
57 | case now := <-ticker.C:
58 | since := now.Sub(start)
59 | t := float64(since%anim.Dur) / float64(anim.Dur)
60 | if !anim.Loop && since >= anim.Dur {
61 | quit <- struct{}{}
62 | t = 1
63 | }
64 | dt := float32(anim.Sig.At(t))
65 | if anim.Interp != nil {
66 | anim.Interp(dt)
67 | }
68 | }
69 | }
70 | }()
71 | return quit
72 | }
73 |
74 | func Animate(mat *f32.Mat4, interp Interpolator, fn func(m *f32.Mat4, dt float32)) (quit chan struct{}) {
75 | m := *mat // copy; translate is always relative to resting position
76 | quit = make(chan struct{}, 1)
77 | go func() {
78 | ticker := time.NewTicker(16 * time.Millisecond)
79 | start := time.Now()
80 | for {
81 | select {
82 | case <-quit:
83 | ticker.Stop()
84 | return
85 | case now := <-ticker.C:
86 | since := now.Sub(start)
87 | t := float64(since%interp.Dur) / float64(interp.Dur)
88 | if !interp.Loop && since >= interp.Dur {
89 | quit <- struct{}{}
90 | t = 1
91 | }
92 | dt := float32(interp.Sig.At(t))
93 | fn(&m, dt)
94 | }
95 | }
96 | }()
97 | return quit
98 | }
99 |
100 | func AnimateRotate(angle f32.Radian, axis f32.Vec3, mat *f32.Mat4, interp Interpolator) (quit chan struct{}) {
101 | return Animate(mat, interp, func(m *f32.Mat4, dt float32) {
102 | mat.Rotate(m, f32.Radian(dt*float32(angle)), &axis)
103 | })
104 | }
105 |
--------------------------------------------------------------------------------
/icon/makeicons.go:
--------------------------------------------------------------------------------
1 | // +build ignore
2 |
3 | package main
4 |
5 | import (
6 | "bytes"
7 | "encoding/xml"
8 | "io/ioutil"
9 | "log"
10 | "os"
11 | "os/exec"
12 | "sort"
13 | "strings"
14 | "text/template"
15 | )
16 |
17 | const tmpl = `// generated by makeicons.go; DO NOT EDIT
18 | package icon
19 |
20 | type Icon int
21 |
22 | func (ic Icon) Texcoords() (float32, float32) {
23 | tc := texcoords[int(ic)]
24 | return tc[0], tc[1]
25 | }
26 |
27 | const ({{range $i, $ic := .Icons}}
28 | {{if eq $i 0}}{{$ic.Name}} Icon = iota{{else}}{{$ic.Name}}{{end}}{{end}}
29 | )
30 |
31 | var texcoords = [][2]float32{
32 | {{range $i, $ic := .Icons}}{{"{"}}{{$ic.X}}, {{$ic.Y}}{{"}"}},{{end}}
33 | }
34 | `
35 |
36 | type Icon struct {
37 | Name string
38 | Set string
39 | X, Y float32
40 | }
41 |
42 | type Set struct {
43 | Name string
44 | Url string
45 | }
46 |
47 | type ByName []Icon
48 |
49 | func (a ByName) Len() int { return len(a) }
50 | func (a ByName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
51 | func (a ByName) Less(i, j int) bool { return a[i].Name < a[j].Name }
52 |
53 | func main() {
54 | gopath := os.Getenv("GOPATH")
55 | iconpath := gopath + "/src/github.com/google/material-design-icons/"
56 | glob := "*/drawable-mdpi/*black_48dp.png"
57 | name := "material-icons-black-mdpi"
58 | cmd := exec.Command("sprity", "create", ".", iconpath+glob, "--margin=0", "--orientation=binary-tree", "--name="+name, "--template=makeicons.hbs", "--style=makeicons.json", "--css-path=''", "--prefix=Foo")
59 | cmd.Stderr = os.Stderr
60 | cmd.Stdout = os.Stdout
61 | if err := cmd.Run(); err != nil {
62 | log.Fatal(err)
63 | }
64 |
65 | f, err := os.Open("makeicons.json")
66 | if err != nil {
67 | log.Fatal(err)
68 | }
69 | dec := xml.NewDecoder(f)
70 |
71 | ics := struct {
72 | Sets []Set `xml:"Set"`
73 | Icons []Icon `xml:"Icon"`
74 | }{}
75 | dec.Decode(&ics)
76 |
77 | for i, ic := range ics.Icons {
78 | name := strings.Split(ic.Name, "-")
79 | a := strings.Title(name[0])
80 | b := strings.Replace(name[3][3:], "_black_48dp", "", 1)
81 | c := strings.Split(b, "_")
82 | for i, s := range c {
83 | c[i] = strings.Title(s)
84 | }
85 | b = strings.Join(c, "")
86 | ic.Name = a + b
87 | ic.X, ic.Y = ic.X/2048, ic.Y/2048
88 | ics.Icons[i] = ic
89 | }
90 |
91 | sort.Sort(ByName(ics.Icons))
92 |
93 | buf := new(bytes.Buffer)
94 | t, err := template.New("").Parse(tmpl)
95 | if err != nil {
96 | log.Fatal(err)
97 | }
98 | t.Execute(buf, ics)
99 | if err := ioutil.WriteFile("icon_mdpi.go", buf.Bytes(), 0644); err != nil {
100 | log.Fatal(err)
101 | }
102 | os.Remove("makeicons.json")
103 | }
104 |
--------------------------------------------------------------------------------
/adaptive.go:
--------------------------------------------------------------------------------
1 | package material
2 |
3 | import (
4 | "golang.org/x/mobile/exp/f32"
5 | "golang.org/x/mobile/gl"
6 | )
7 |
8 | type Behavior int
9 |
10 | const (
11 | // When screen space is available, a surface is always visible.
12 | VisibilityPermanent Behavior = iota
13 |
14 | // Surface visibility can be toggled between visible and hidden. When visible,
15 | // interacting with other elements on the screen does not change visibility.
16 | VisibilityPersistent
17 |
18 | // Surface visibility can be toggled between visible and hidden. When visible,
19 | // interacting with other elements on the screen toggles the surface to become
20 | // hidden or minimized.
21 | VisibilityTemporary
22 |
23 | // Element width stays the same when screen size changes.
24 | WidthFixed
25 |
26 | // Element width grows as screen size changes.
27 | WidthFluid
28 |
29 | // Element width is fixed until influenced by another element or breakpoint.
30 | WidthSticky
31 |
32 | // Element width contracts as a panel is revealed
33 | WidthSqueeze
34 |
35 | // Element width stays the same, its position changes horizontally as a panel
36 | // appears, and it may be partially occluded by a screen’s edge.
37 | WidthPush
38 |
39 | // Element width and position stays the same as a panel appears over content.
40 | WidthOverlay
41 |
42 | // The z position, and shadow of an element. A flat element will have no shadow.
43 | DescriptorFlat
44 | DescriptorRaised
45 | )
46 |
47 | type Grid struct {
48 | Margin float32
49 | Gutter float32
50 | Columns int
51 |
52 | debug *Material
53 | }
54 |
55 | func (gd *Grid) StepSize() float32 {
56 | return (float32(windowSize.WidthPx) - (gd.Margin * 2)) / float32(gd.Columns)
57 | }
58 |
59 | // TODO avoid the pointer
60 | func NewGrid() *Grid {
61 | // by breakpoints
62 | g := &Grid{Margin: 24, Gutter: 24, Columns: 12}
63 | if windowSize.WidthPx < int(Dp(600).Px()) || windowSize.HeightPx < int(Dp(600).Px()) {
64 | g.Margin, g.Gutter = 16, 16 // TODO dp vals
65 | }
66 | if windowSize.WidthPx < int(Dp(480).Px()) {
67 | g.Columns = 4
68 | } else if windowSize.WidthPx < int(Dp(720).Px()) {
69 | g.Columns = 8
70 | }
71 | return g
72 | }
73 |
74 | func (gd *Grid) draw(ctx gl.Context, view, proj f32.Mat4) {
75 | if gd.debug == nil {
76 | gd.debug = New(ctx, Color(0x03A9F499))
77 | gd.debug.world.Identity()
78 | gd.debug.world[0][0] = gd.Gutter + gd.Margin
79 | gd.debug.world[1][1] = float32(windowSize.HeightPx)
80 | }
81 |
82 | step := gd.StepSize()
83 | for i := 0; i <= gd.Columns; i++ {
84 | if i == 0 {
85 | gd.debug.world[0][0] = gd.Margin
86 | gd.debug.world[0][3] = 0
87 | } else if i == gd.Columns {
88 | gd.debug.world[0][0] = gd.Margin
89 | gd.debug.world[0][3] = gd.Margin + float32(i)*step
90 | } else {
91 | gd.debug.world[0][0] = gd.Gutter
92 | gd.debug.world[0][3] = gd.Margin + float32(i)*step - gd.Gutter/2.0
93 | }
94 | // TODO add to environment
95 | // gd.debug.Draw(ctx, view, proj)
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/atlas/atlas.go:
--------------------------------------------------------------------------------
1 | package atlas
2 |
3 | import (
4 | "fmt"
5 | "image"
6 | "image/color"
7 | "image/draw"
8 | "image/png"
9 | "os"
10 | "sort"
11 |
12 | "dasa.cc/material/glutil"
13 | "dasa.cc/x/octree"
14 | "golang.org/x/mobile/gl"
15 | )
16 |
17 | var (
18 | DefaultFilter = glutil.TextureFilter(gl.LINEAR, gl.LINEAR)
19 | DefaultWrap = glutil.TextureWrap(gl.REPEAT, gl.REPEAT)
20 | )
21 |
22 | type Atlas struct {
23 | tex glutil.Texture
24 | img *image.NRGBA
25 | regions []image.Rectangle
26 | // codes []image.Rectangle
27 | }
28 |
29 | func New(w, h int) *Atlas {
30 | r := image.Rect(0, 0, w, h)
31 | return &Atlas{
32 | img: image.NewNRGBA(r),
33 | regions: []image.Rectangle{r},
34 | }
35 | }
36 |
37 | func (atlas *Atlas) Create(ctx gl.Context) {
38 | atlas.tex.Create(ctx)
39 | }
40 |
41 | func (atlas *Atlas) Bind(ctx gl.Context) {
42 | atlas.tex.Bind(ctx, DefaultFilter, DefaultWrap)
43 | }
44 |
45 | func (atlas *Atlas) Update(ctx gl.Context) {
46 | if atlas.tex.Value == gl.TEXTURE0 {
47 | atlas.tex.Create(ctx)
48 | atlas.tex.Bind(ctx, DefaultFilter, DefaultWrap)
49 | atlas.tex.Update(ctx, 0, atlas.img.Bounds().Dx(), atlas.img.Bounds().Dy(), atlas.img.Pix)
50 | } else {
51 | atlas.tex.Bind(ctx, DefaultFilter, DefaultWrap)
52 | atlas.tex.Sub(ctx, 0, atlas.img.Bounds().Dx(), atlas.img.Bounds().Dy(), atlas.img.Pix)
53 | }
54 | }
55 |
56 | func (atlas *Atlas) Add(src image.Image) (image.Rectangle, error) {
57 | // first fit decreasing
58 | // TODO look at improved bin packing to replace this later on:
59 | // http://moose.cs.ucla.edu/publications/schreiber_korf_ijcai13.pdf
60 | sz := image.Rectangle{Max: src.Bounds().Size()}
61 | for i, r := range atlas.regions {
62 | s := sz.Add(r.Min)
63 | if s.In(r) {
64 | draw.Draw(atlas.img, s, src, src.Bounds().Min, draw.Over)
65 | // atlas.codes = append(atlas.codes, s)
66 |
67 | right, bottom := r, r
68 | right.Min.X += sz.Max.X
69 | bottom.Min.Y += sz.Max.Y
70 | if dt := r.Max.Sub(sz.Max); dt.X > dt.Y {
71 | bottom.Max.X = right.Min.X
72 | } else {
73 | right.Max.Y = bottom.Min.Y
74 | }
75 | atlas.regions = append(atlas.regions[:i], atlas.regions[i+1:]...)
76 | atlas.regions = append(atlas.regions, right, bottom)
77 | sort.Sort(sort.Reverse(byArea(atlas.regions)))
78 | return s, nil
79 | }
80 | }
81 |
82 | return image.ZR, fmt.Errorf("no available space to add image with size %+v", sz.Max)
83 | }
84 |
85 | func (atl *Atlas) writeFile(name string) error {
86 | out, err := os.Create(name)
87 | if err != nil {
88 | return err
89 | }
90 | defer out.Close()
91 |
92 | m := image.NewNRGBA(atl.img.Bounds())
93 | draw.Draw(m, m.Bounds(), image.NewUniform(color.White), image.ZP, draw.Src)
94 | draw.Draw(m, m.Bounds(), atl.img, image.ZP, draw.Over)
95 |
96 | return png.Encode(out, m)
97 | }
98 |
99 | type byArea []image.Rectangle
100 |
101 | func (a byArea) Len() int { return len(a) }
102 | func (a byArea) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
103 | func (a byArea) Less(i, j int) bool {
104 | p, q := a[i].Size(), a[j].Size()
105 | return p.X*p.Y < q.X*q.Y
106 | }
107 |
108 | func interleave(x0, y0, x1, y1 uint16) uint64 {
109 | return octree.Dilate16(y1) | octree.Dilate16(x1)<<1 | octree.Dilate16(y0)<<2 | octree.Dilate16(x0)<<3
110 | }
111 |
112 | func deinterleave(a uint64) (x0, y0, x1, y1 uint16) {
113 | return octree.Undilate16(a >> 3), octree.Undilate16(a >> 2), octree.Undilate16(a >> 1), octree.Undilate16(a)
114 | }
115 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # material
2 |
3 | This package is a work-in-progress for providing an implementation of material design for gomobile and potentially exp/shiny.
4 |
5 | The core goal of this package is to provide an implementation of material design as defined here: https://www.google.com/design/spec/material-design/introduction.html
6 |
7 | Nothing more.
8 |
9 | Features currently provided were written to determine the exact nature of moving forward with a proper implementation. These include the following:
10 |
11 | * Constraint based layouts using simplex method. See https://github.com/dskinner/simplex package. A trivial box model has been written on top of this. The intended usage of the constraint based layout is to fulfill the adaptive layout requirements of material design.
12 |
13 | * Key light shadows. These shadows are not approximated on the material. This can be seen by tilting a view matrix and inspecting. The current implementation is fairly performant on mid ranged android devices.
14 |
15 | * Material design icons, mdpi. See material/icon package. This currently provides 913 icons at 48x48 px as a single texture. While not suitable for hires display, this is more than suitable for development of the package itself. A script in material/icon can be expanded to provide desired sets of icons at high resolution. Grab a copy of the texture here: https://drive.google.com/a/dasa.cc/file/d/0B6hxg-gC2Uz_cG1DakFNcDFxYlk/view
16 |
17 | * Text is provided with signed-distance-field texture. See material/text source for generating a texture and material/example/text for usage.
18 |
19 | * Material. This also includes 1dp of thickness, though the exact nature of this fades when transforming rectangles to ellipses. Only two behavior flags are currently recognized declaring material flat (transparent) or raised (has shadow).
20 |
21 | * Color. A full list of colors as defined is the spec is available as uint32s, e.g. BlueGrey500
22 |
23 | * Environment. While more of an abstraction in the design spec, there is an environment type that accepts a theme palette and is used for creation of new material types such as button and toolbar.
24 |
25 | ## Roadmap
26 |
27 | One of the most important aspects of any UI is to have an effective way to layout items. Such a solution is purely numerical. With that in mind, the current focus is to provide an implementation of material design's [Adaptive UI](https://www.google.com/design/spec/layout/adaptive-ui.html). The implementation should not be interdependent on any other portion of package material. It's conceivable for example to make use of such a package for reflowing terminal based UIs.
28 |
29 | The package shall be well developed, documented, and tested. There are enough minimal implementations of other portions of material design to provide meaningful examples of such a package once complete. Only upon completion of Adaptive UI will a determination of the next step be made.
30 |
31 | As for a timeframe on the whole of package material with only the spare time of a single developer, I could only wildly guesstimate a time frame of 2+ years for completion. Part of this is a strong urge to take a slow-and-steady pace for each portion of package material to best ensure design choices, proper documentation, and reasonably complete testing.
32 |
33 | ## Contributing
34 |
35 | Everything in this package is in-flux. If you're interested in contributing, understand the current focus is iterating on the currently provided features. New features will not be accepted unless they are important for establishing a baseline in determining future functionality and api. Anything beyond the material design spec is out of scope for this package and will not be accepted.
36 |
37 | Please open an issue and discuss your thoughts first.
38 |
--------------------------------------------------------------------------------
/layout.go:
--------------------------------------------------------------------------------
1 | package material
2 |
3 | import (
4 | "dasa.cc/simplex"
5 | "golang.org/x/mobile/exp/f32"
6 | )
7 |
8 | type Box struct {
9 | l, r, b, t, z simplex.Var
10 | world f32.Mat4
11 | }
12 |
13 | func NewBox(prg *simplex.Program) (a Box) {
14 | a.l, a.r, a.b, a.t, a.z = prg.Var(1), prg.Var(1), prg.Var(1), prg.Var(1), prg.Var(1)
15 | return
16 | }
17 |
18 | func (a Box) Width(x float32) simplex.Constraint {
19 | return simplex.Constrain(simplex.Coef{1, a.r}, simplex.Coef{-1, a.l}).Equal(float64(x))
20 | }
21 |
22 | func (a Box) Height(x float32) simplex.Constraint {
23 | return simplex.Constrain(simplex.Coef{1, a.t}, simplex.Coef{-1, a.b}).Equal(float64(x))
24 | }
25 |
26 | func (a Box) Start(x float32) simplex.Constraint {
27 | return simplex.Constrain(simplex.Coef{1, a.l}).Equal(float64(x))
28 | }
29 |
30 | func (a Box) End(x float32) simplex.Constraint {
31 | return simplex.Constrain(simplex.Coef{1, a.r}).Equal(float64(x))
32 | }
33 |
34 | func (a Box) Bottom(x float32) simplex.Constraint {
35 | return simplex.Constrain(simplex.Coef{1, a.b}).Equal(float64(x))
36 | }
37 |
38 | func (a Box) Top(x float32) simplex.Constraint {
39 | return simplex.Constrain(simplex.Coef{1, a.t}).Equal(float64(x))
40 | }
41 |
42 | func (a Box) Z(z float32) simplex.Constraint {
43 | return simplex.Constrain(simplex.Coef{1, a.z}).Equal(float64(z))
44 | }
45 |
46 | func (a Box) StartIn(b Box, by float32) simplex.Constraint {
47 | return simplex.Constrain(simplex.Coef{1, a.l}, simplex.Coef{-1, b.l}).GreaterEq(float64(by))
48 | }
49 |
50 | func (a Box) EndIn(b Box, by float32) simplex.Constraint {
51 | return simplex.Constrain(simplex.Coef{1, b.r}, simplex.Coef{-1, a.r}).GreaterEq(float64(by))
52 | }
53 |
54 | func (a Box) BottomIn(b Box, by float32) simplex.Constraint {
55 | return simplex.Constrain(simplex.Coef{1, a.b}, simplex.Coef{-1, b.b}).GreaterEq(float64(by))
56 | }
57 |
58 | func (a Box) TopIn(b Box, by float32) simplex.Constraint {
59 | return simplex.Constrain(simplex.Coef{1, b.t}, simplex.Coef{-1, a.t}).GreaterEq(float64(by))
60 | }
61 |
62 | func (a Box) CenterVerticalIn(b Box) simplex.Constraint {
63 | return simplex.Constrain(simplex.Coef{1, b.b}, simplex.Coef{1, b.t}, simplex.Coef{-1, a.b}, simplex.Coef{-1, a.t})
64 | }
65 |
66 | func (a Box) CenterHorizontalIn(b Box) simplex.Constraint {
67 | return simplex.Constrain(simplex.Coef{1, b.l}, simplex.Coef{1, b.r}, simplex.Coef{-1, a.l}, simplex.Coef{-1, a.r})
68 | }
69 |
70 | func (a Box) Before(b Box, by float32) simplex.Constraint {
71 | return simplex.Constrain(simplex.Coef{1, b.l}, simplex.Coef{-1, a.r}).GreaterEq(float64(by))
72 | }
73 |
74 | func (a Box) After(b Box, by float32) simplex.Constraint {
75 | // TODO this is the crux of adaptive layout model, along with a Before method.
76 | // Consider how box a would be after box b if room, otherwise box a is below box b.
77 | // Note in the latter case, box a should not be aligned after box b when below.
78 | return simplex.Constrain(simplex.Coef{1, a.l}, simplex.Coef{-1, b.r}).GreaterEq(float64(by))
79 | }
80 |
81 | func (a Box) Below(b Box, by float32) simplex.Constraint {
82 | return simplex.Constrain(simplex.Coef{1, b.b}, simplex.Coef{-1, a.t}).GreaterEq(float64(by))
83 | }
84 |
85 | func (a Box) Above(b Box, by float32) simplex.Constraint {
86 | return simplex.Constrain(simplex.Coef{1, a.b}, simplex.Coef{-1, b.t}).GreaterEq(float64(by))
87 | }
88 |
89 | func (a Box) AlignBottoms(b Box, by float32) simplex.Constraint {
90 | return simplex.Constrain(simplex.Coef{1, b.b}, simplex.Coef{-1, a.b}).GreaterEq(float64(by))
91 | }
92 |
93 | func (a Box) AlignTops(b Box, by float32) simplex.Constraint {
94 | return simplex.Constrain(simplex.Coef{1, b.t}, simplex.Coef{-1, a.t}).GreaterEq(float64(by))
95 | }
96 |
97 | func (a Box) Bounds(l, r, b, t float32) []simplex.Constraint {
98 | return []simplex.Constraint{
99 | simplex.Constrain(simplex.Coef{1, a.l}).GreaterEq(float64(l)),
100 | simplex.Constrain(simplex.Coef{1, a.r}).LessEq(float64(r)),
101 | simplex.Constrain(simplex.Coef{1, a.b}).GreaterEq(float64(b)),
102 | simplex.Constrain(simplex.Coef{1, a.t}).LessEq(float64(t)),
103 | }
104 | }
105 |
106 | func (a *Box) UpdateWorld(prg *simplex.Program) {
107 | prg.For(&a.l, &a.r, &a.b, &a.t, &a.z)
108 | a.world.Identity()
109 | a.world.Translate(&a.world, float32(a.l.Val), float32(a.b.Val), 0)
110 | a.world.Scale(&a.world, float32(a.r.Val-a.l.Val), float32(a.t.Val-a.b.Val), 1)
111 | a.world[2][3] = float32(a.z.Val)
112 | }
113 |
--------------------------------------------------------------------------------
/example/text/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "log"
5 | "time"
6 |
7 | "dasa.cc/material"
8 | "golang.org/x/mobile/app"
9 | "golang.org/x/mobile/event/lifecycle"
10 | "golang.org/x/mobile/event/paint"
11 | "golang.org/x/mobile/event/size"
12 | "golang.org/x/mobile/event/touch"
13 | "golang.org/x/mobile/gl"
14 | )
15 |
16 | var (
17 | env = new(material.Environment)
18 |
19 | t112, t56, t45, t34, t24, t20, t16, t14, t12 *material.Button
20 | )
21 |
22 | func init() {
23 | env.SetPalette(material.Palette{
24 | Primary: material.BlueGrey500,
25 | Dark: material.BlueGrey700,
26 | Light: material.BlueGrey100,
27 | Accent: material.DeepOrangeA200,
28 | })
29 | }
30 |
31 | func onStart(ctx gl.Context) {
32 | ctx.Enable(gl.BLEND)
33 | ctx.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
34 | ctx.Enable(gl.CULL_FACE)
35 | ctx.CullFace(gl.BACK)
36 |
37 | env.Load(ctx)
38 | env.LoadGlyphs(ctx)
39 |
40 | t112 = env.NewButton(ctx)
41 | t112.SetTextColor(material.White)
42 | t112.SetText("AAAH`e_llo |jJ go 112px")
43 | t112.BehaviorFlags = material.DescriptorFlat
44 |
45 | t56 = env.NewButton(ctx)
46 | t56.SetTextColor(material.White)
47 | t56.SetText("Hello go 56px")
48 | t56.BehaviorFlags = material.DescriptorFlat
49 |
50 | t45 = env.NewButton(ctx)
51 | t45.SetTextColor(material.White)
52 | t45.SetText("Hello go 45px")
53 | t45.BehaviorFlags = material.DescriptorFlat
54 |
55 | t34 = env.NewButton(ctx)
56 | t34.SetTextColor(material.White)
57 | t34.SetText("Hello go 34px")
58 | t34.BehaviorFlags = material.DescriptorFlat
59 |
60 | t24 = env.NewButton(ctx)
61 | t24.SetTextColor(material.White)
62 | t24.SetText("Hello go 24px")
63 | t24.BehaviorFlags = material.DescriptorFlat
64 |
65 | t20 = env.NewButton(ctx)
66 | t20.SetTextColor(material.White)
67 | t20.SetText("Hello go 20px")
68 | t20.BehaviorFlags = material.DescriptorFlat
69 |
70 | t16 = env.NewButton(ctx)
71 | t16.SetTextColor(material.White)
72 | t16.SetText("Hello go 16px")
73 | t16.BehaviorFlags = material.DescriptorFlat
74 |
75 | t14 = env.NewButton(ctx)
76 | t14.SetTextColor(material.White)
77 | t14.SetText("Hello go 14px")
78 | t14.BehaviorFlags = material.DescriptorFlat
79 |
80 | t12 = env.NewButton(ctx)
81 | t12.SetTextColor(material.White)
82 | t12.SetText("Hello go 12px")
83 | t12.BehaviorFlags = material.DescriptorFlat
84 | }
85 |
86 | func onLayout(sz size.Event) {
87 | env.SetOrtho(sz)
88 | env.StartLayout()
89 | env.AddConstraints(
90 | t112.Width(1290), t112.Height(112), t112.Z(1), t112.StartIn(env.Box, env.Grid.Gutter), t112.TopIn(env.Box, env.Grid.Gutter),
91 | t56.Width(620), t56.Height(56), t56.Z(1), t56.StartIn(env.Box, env.Grid.Gutter), t56.Below(t112.Box, env.Grid.Gutter),
92 | t45.Width(500), t45.Height(45), t45.Z(1), t45.StartIn(env.Box, env.Grid.Gutter), t45.Below(t56.Box, env.Grid.Gutter),
93 | t34.Width(380), t34.Height(34), t34.Z(1), t34.StartIn(env.Box, env.Grid.Gutter), t34.Below(t45.Box, env.Grid.Gutter),
94 | t24.Width(270), t24.Height(24), t24.Z(1), t24.StartIn(env.Box, env.Grid.Gutter), t24.Below(t34.Box, env.Grid.Gutter),
95 | t20.Width(230), t20.Height(20), t20.Z(1), t20.StartIn(env.Box, env.Grid.Gutter), t20.Below(t24.Box, env.Grid.Gutter),
96 | t16.Width(180), t16.Height(16), t16.Z(1), t16.StartIn(env.Box, env.Grid.Gutter), t16.Below(t20.Box, env.Grid.Gutter),
97 | t14.Width(155), t14.Height(14), t14.Z(1), t14.StartIn(env.Box, env.Grid.Gutter), t14.Below(t16.Box, env.Grid.Gutter),
98 | t12.Width(135), t12.Height(12), t12.Z(1), t12.StartIn(env.Box, env.Grid.Gutter), t12.Below(t14.Box, env.Grid.Gutter),
99 | )
100 | log.Println("starting layout")
101 | t := time.Now()
102 | env.FinishLayout()
103 | log.Printf("finished layout in %s\n", time.Now().Sub(t))
104 | }
105 |
106 | var lastpaint time.Time
107 | var fps int
108 |
109 | func onPaint(ctx gl.Context) {
110 | ctx.ClearColor(material.BlueGrey500.RGBA())
111 | ctx.Clear(gl.COLOR_BUFFER_BIT)
112 | env.Draw(ctx)
113 | now := time.Now()
114 | fps = int(time.Second / now.Sub(lastpaint))
115 | lastpaint = now
116 | }
117 |
118 | func main() {
119 | app.Main(func(a app.App) {
120 | var glctx gl.Context
121 | for ev := range a.Events() {
122 | switch ev := a.Filter(ev).(type) {
123 | case lifecycle.Event:
124 | switch ev.Crosses(lifecycle.StageVisible) {
125 | case lifecycle.CrossOn:
126 | go func() {
127 | for range time.Tick(time.Second) {
128 | log.Printf("fps=%-4v\n", fps)
129 | }
130 | }()
131 | glctx = ev.DrawContext.(gl.Context)
132 | onStart(glctx)
133 | a.Send(paint.Event{})
134 | case lifecycle.CrossOff:
135 | env.Unload(glctx)
136 | glctx = nil
137 | }
138 | case size.Event:
139 | if glctx == nil {
140 | a.Send(ev) // republish event until onStart is called
141 | } else {
142 | onLayout(ev)
143 | }
144 | case touch.Event:
145 | env.Touch(ev)
146 | case paint.Event:
147 | if glctx == nil || ev.External {
148 | continue
149 | }
150 | onPaint(glctx)
151 | a.Publish()
152 | a.Send(paint.Event{})
153 | }
154 | }
155 | })
156 | }
157 |
--------------------------------------------------------------------------------
/glutil/glutil.go:
--------------------------------------------------------------------------------
1 | package glutil
2 |
3 | import (
4 | "math"
5 |
6 | "golang.org/x/mobile/exp/f32"
7 | "golang.org/x/mobile/gl"
8 | )
9 |
10 | var ident f32.Mat4
11 |
12 | func init() {
13 | ident.Identity()
14 | }
15 |
16 | // Ortho provides a general purpose orthographic projection.
17 | // TODO probably just get rid of this and pass in zero'd out z to perspective
18 | func Ortho(m *f32.Mat4, l, r float32, b, t float32, n, f float32) {
19 | m.Identity()
20 | m.Scale(m, 2/(r-l), 2/(t-b), 2/(f-n))
21 | m.Translate(m, -((l + r) / 2), -((t + b) / 2), (f+n)/2)
22 | }
23 |
24 | // Perspective sets m to a screen space perspective with origin at bottom-left.
25 | func Perspective(m *f32.Mat4, l, r float32, b, t float32) {
26 | m.Identity()
27 | // TODO i think [2][2] is best as t-b, or, shortest path, but maybe worth picking something that's consistent
28 | // e.g. always 1000
29 | (*m)[0][0] = 2 / (r - l)
30 | (*m)[0][3] = -1 // offset for [0][0]
31 | (*m)[1][1] = 2 / (t - b)
32 | (*m)[1][3] = -1 // offset for [1][1]
33 | (*m)[2][2] = 2 / (r * 10) //(r - l) // TODO should maybe pick consistent result, such as whichever is smaller; r-l or t-b
34 | (*m)[3][2] = -(*m)[2][2] * 10 // pronounced z effect with increased factor
35 | }
36 |
37 | type Drawer interface {
38 | Draw(ctx gl.Context, view, proj f32.Mat4)
39 | }
40 |
41 | type DrawerFunc func(ctx gl.Context, view, proj f32.Mat4)
42 |
43 | func (fn DrawerFunc) Draw(ctx gl.Context, view, proj f32.Mat4) {
44 | fn(ctx, view, proj)
45 | }
46 |
47 | type FloatBuffer interface {
48 | Bind(gl.Context)
49 | Update(gl.Context, []float32)
50 | Draw(gl.Context, Program, gl.Enum)
51 | Delete(gl.Context)
52 | }
53 |
54 | type floatBuffer struct {
55 | gl.Buffer
56 | bin []byte
57 | count int
58 | usage gl.Enum
59 | }
60 |
61 | func NewFloatBuffer(ctx gl.Context, data []float32, usage gl.Enum) FloatBuffer {
62 | buf := &floatBuffer{Buffer: ctx.CreateBuffer(), usage: usage}
63 | buf.Bind(ctx)
64 | buf.Update(ctx, data)
65 | return buf
66 | }
67 |
68 | func (buf *floatBuffer) Bind(ctx gl.Context) { ctx.BindBuffer(gl.ARRAY_BUFFER, buf.Buffer) }
69 |
70 | func (buf *floatBuffer) Update(ctx gl.Context, data []float32) {
71 | buf.count = len(data)
72 | subok := len(buf.bin) > 0 && len(data)*4 <= len(buf.bin)
73 | if !subok {
74 | buf.bin = make([]byte, len(data)*4)
75 | }
76 | for i, x := range data {
77 | u := math.Float32bits(x)
78 | buf.bin[4*i+0] = byte(u >> 0)
79 | buf.bin[4*i+1] = byte(u >> 8)
80 | buf.bin[4*i+2] = byte(u >> 16)
81 | buf.bin[4*i+3] = byte(u >> 24)
82 | }
83 | if subok {
84 | ctx.BufferSubData(gl.ARRAY_BUFFER, 0, buf.bin)
85 | } else {
86 | ctx.BufferData(gl.ARRAY_BUFFER, buf.bin, buf.usage)
87 | }
88 | }
89 |
90 | func (buf *floatBuffer) Draw(ctx gl.Context, prg Program, mode gl.Enum) {
91 | ctx.DrawArrays(mode, 0, buf.count)
92 | }
93 |
94 | func (buf *floatBuffer) Delete(ctx gl.Context) {
95 | ctx.DeleteBuffer(buf.Buffer)
96 | }
97 |
98 | type UintBuffer interface {
99 | Bind(gl.Context)
100 | Update(gl.Context, []uint32)
101 | Draw(gl.Context, Program, gl.Enum)
102 | Delete(gl.Context)
103 | }
104 |
105 | type uintBuffer struct {
106 | gl.Buffer
107 | bin []byte
108 | count int
109 | usage gl.Enum
110 | }
111 |
112 | func NewUintBuffer(ctx gl.Context, data []uint32, usage gl.Enum) UintBuffer {
113 | buf := &uintBuffer{Buffer: ctx.CreateBuffer(), usage: usage}
114 | buf.Bind(ctx)
115 | buf.Update(ctx, data)
116 | return buf
117 | }
118 |
119 | func (buf *uintBuffer) Bind(ctx gl.Context) { ctx.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, buf.Buffer) }
120 |
121 | func (buf *uintBuffer) Update(ctx gl.Context, data []uint32) {
122 | buf.count = len(data)
123 | subok := len(buf.bin) > 0 && len(data)*4 <= len(buf.bin)
124 | if !subok {
125 | buf.bin = make([]byte, len(data)*4)
126 | }
127 | for i, u := range data {
128 | buf.bin[4*i+0] = byte(u >> 0)
129 | buf.bin[4*i+1] = byte(u >> 8)
130 | buf.bin[4*i+2] = byte(u >> 16)
131 | buf.bin[4*i+3] = byte(u >> 24)
132 | }
133 | if subok {
134 | ctx.BufferSubData(gl.ELEMENT_ARRAY_BUFFER, 0, buf.bin)
135 | } else {
136 | ctx.BufferData(gl.ELEMENT_ARRAY_BUFFER, buf.bin, buf.usage)
137 | }
138 | }
139 |
140 | func (buf *uintBuffer) Draw(ctx gl.Context, prg Program, mode gl.Enum) {
141 | ctx.DrawElements(mode, buf.count, gl.UNSIGNED_INT, 0)
142 | }
143 |
144 | func (buf *uintBuffer) Delete(ctx gl.Context) {
145 | ctx.DeleteBuffer(buf.Buffer)
146 | }
147 |
148 | var (
149 | int32v4 = make([]int32, 4)
150 | int32v2 = make([]int32, 2)
151 | )
152 |
153 | type TextureFramebuffer struct {
154 | fbo Framebuffer
155 | tex Texture
156 | w, h int
157 |
158 | def, filter, wrap func(gl.Context, Texture)
159 | withtex func(gl.Context, Framebuffer)
160 | }
161 |
162 | func (buf *TextureFramebuffer) Tex() Texture { return buf.tex }
163 |
164 | // TODO pass in an actual Texture ...
165 | func NewTextureBuffer(ctx gl.Context, width, height int) *TextureFramebuffer {
166 | buf := &TextureFramebuffer{w: width, h: height}
167 | buf.fbo = Framebuffer{ctx.CreateFramebuffer()}
168 | buf.tex = Texture{ctx.CreateTexture()}
169 |
170 | buf.def = TextureDef(0, width, height, gl.RGBA, nil)
171 | buf.filter = TextureFilter(gl.LINEAR, gl.LINEAR)
172 | buf.wrap = TextureWrap(gl.REPEAT, gl.REPEAT)
173 | buf.withtex = FramebufferWithTex(buf.tex, 0, buf.def, buf.filter, buf.wrap)
174 | return buf
175 | }
176 |
177 | func (buf *TextureFramebuffer) Delete(ctx gl.Context) {
178 | ctx.DeleteFramebuffer(buf.fbo.Framebuffer)
179 | ctx.DeleteTexture(buf.tex.Texture)
180 | }
181 |
182 | func (buf *TextureFramebuffer) StartSample(ctx gl.Context) {
183 | buf.fbo.Bind(ctx, buf.withtex)
184 | ctx.GetIntegerv(int32v4, gl.VIEWPORT)
185 | ctx.Viewport(0, 0, buf.w, buf.h)
186 | ctx.ClearColor(0, 0, 0, 0)
187 | ctx.Clear(gl.COLOR_BUFFER_BIT)
188 | }
189 |
190 | func (buf *TextureFramebuffer) StopSample(ctx gl.Context) {
191 | ctx.Viewport(int(int32v4[0]), int(int32v4[1]), int(int32v4[2]), int(int32v4[3]))
192 | // TODO Unbind should maybe take options too?
193 | buf.fbo.Unbind(ctx)
194 | // sbuf.tex.Unbind(ctx)
195 | }
196 |
--------------------------------------------------------------------------------
/assets/environment-frag.glsl:
--------------------------------------------------------------------------------
1 | #version 100
2 | #define onesqrt2 0.70710678118
3 | #define sqrt2 1.41421356237
4 | #define pi 3.14159265359
5 | #define twopi 6.28318530718
6 |
7 | #define touchBegin 0.0
8 | #define touchMove 1.0
9 | #define touchEnd 2.0
10 | precision mediump float;
11 |
12 | // TODO pass this in some other way so sampler can be selected
13 | // 0:fontsize, 1:pad, 2:edge
14 | uniform vec4 glyphconf;
15 |
16 | uniform sampler2D image;
17 |
18 | uniform sampler2D texglyph;
19 | uniform sampler2D texicon;
20 | uniform vec2 glyph;
21 | uniform vec4 shadowColor;
22 |
23 | varying vec4 vtexcoord;
24 | varying vec4 vvertex;
25 | varying vec4 vtouch;
26 |
27 | // interpolated distance, and size values
28 | // x, y is unit value [0..1]
29 | // z, w is material width, height
30 | varying vec4 vdist;
31 | varying vec4 vcolor;
32 |
33 | vec4 sampleIcon() {
34 | vec4 clr = texture2D(texicon, vtexcoord.xy);
35 | clr.rgb += vcolor.rgb;
36 | clr.a *= 0.54; // https://www.google.com/design/spec/style/color.html#color-ui-color-application
37 | return clr;
38 | }
39 |
40 | vec4 sampleImage() {
41 | vec4 clr = texture2D(image, vtexcoord.xy);
42 | return clr;
43 | }
44 |
45 | // four values for a texture might look like this
46 | // [
47 | // morton-encoded bounds,
48 | // texture size,
49 | // ?type? so as to invoke specific shader bits (glyph, icon, image)
50 | // alpha
51 | // ]
52 | //
53 | // numbers are 32-bit
54 | // must have four values to id rectangle for texture coordinates.
55 | // could reduce this to 3 numbers if two were uint16 containers and one
56 | // specified texture size. If texture size could be set only once then
57 | // it could be two. Perhaps set texture size in a uniform? that would
58 | // allow for 2 numbers to specify rectangle and 1 for type and 1 for alpha.
59 |
60 | vec4 sampleGlyph() {
61 | float fontsize = glyphconf.x;
62 | float pad = glyphconf.y;
63 | float edge = glyphconf.z;
64 |
65 | float d = texture2D(texglyph, vtexcoord.xy).a;
66 | float h = vdist.w;
67 | float gamma = 0.22/(pad*(h/fontsize));
68 |
69 | // d += 0.2; // bold
70 | // d -= 0.2; // thin
71 |
72 | vec4 clr = vcolor;
73 | clr.a = smoothstep(edge-gamma, edge+gamma, d);
74 | clr.a *= 0.87; // secondary text
75 | return clr;
76 | }
77 |
78 | // TODO drop this
79 | bool shouldcolor(vec2 pos, float sz) {
80 | // maps 0.0 .. 0.5 .. 1.0
81 | // to 0.0 .. 0.5 .. 0.0
82 | pos = 0.5-abs(pos-0.5);
83 |
84 | // multiply by width/height
85 | pos *= vdist.zw;
86 |
87 | //
88 | if (pos.x <= sz && pos.y <= sz) {
89 | float d = length(1.0-(pos/sz));
90 | if (d > 1.0) {
91 | return false;
92 | }
93 | }
94 | return true;
95 | }
96 |
97 | float shade(vec2 pos, float sz) {
98 | pos = 0.5-abs(pos-0.5);
99 | pos *= vdist.zw;
100 |
101 | if (pos.x <= sz && pos.y <= sz) {
102 | float d = length(1.0-(pos/sz));
103 | if (d > 1.0) { // TODO consider moving this as discard into top of main
104 | return 0.0;
105 | }
106 | return 1.0-d;
107 | } else if (pos.x <= sz && pos.y > sz) {
108 | return pos.x/sz;
109 | } else if (pos.x > sz && pos.y <= sz) {
110 | return pos.y/sz;
111 | }
112 | return 1.0;
113 | }
114 |
115 | void main() {
116 | float roundness = vvertex.w;
117 |
118 | if (vtexcoord.x >= 0.0) {
119 | if (vtexcoord.z == 3.0) {
120 | gl_FragColor = sampleImage();
121 | } else if (vtexcoord.z == 1.0) {
122 | gl_FragColor = sampleIcon();
123 | } else {
124 | gl_FragColor = sampleGlyph();
125 | }
126 | } else if (vvertex.z <= 0.0) { // draw shadow
127 | if (roundness < 8.0) {
128 | roundness = 8.0;
129 | }
130 |
131 | // TODO ellipsis are being over-rounded resulting in a distortion
132 | // in the shadow. The distortion isn't very noticable unless drawing
133 | // only shadows, but this does affect outline of material making it
134 | // more difficult to see material edges. Clamping roundness results
135 | // in a strange distortion when animating roundness and the cause is
136 | // not currently clear.
137 | //
138 | // At the same time, the distortion looks better for different cases.
139 | roundness += -vvertex.z;
140 | // if (roundness > vdist.z/2.0) {
141 | // roundness = vdist.z/2.0;
142 | // }
143 |
144 | gl_FragColor = shadowColor;
145 |
146 | // maps roundness to 1 - [0..0.66] and helps shadows cast by rectangles and ellipses
147 | // look similar at the same z index.
148 | // TODO vvertex.w is roundness, double check usage here as was used before float roundness declared
149 | float e = 1.0 - (vvertex.w/vdist.z/0.75);
150 |
151 | gl_FragColor.a = smoothstep(0.0, e, shade(vdist.xy, roundness));
152 | vec2 n = abs(vdist.xy*2.0 - 1.0);
153 | n = n*n*n*n;
154 | n = 1.0-n;
155 |
156 | // reduce alpha/strength as z-index increases
157 | float f = 1.0 + (-vvertex.z*0.1);
158 |
159 | gl_FragColor.a *= n.x*n.y/f;
160 | // gl_FragColor.a *= 10.0;
161 | } else { // draw material
162 | if (shouldcolor(vdist.xy, roundness)) {
163 | gl_FragColor = vcolor;
164 |
165 | // anti-alias roundness
166 | if (gl_FragColor.a != 0.0) { // guard against exposing bg of text and icon content
167 | float dist = 1.0-shade(vdist.xy, roundness);
168 | // fractional based on largest size, approximates a consistent value across resolutions
169 | float dt = (5.0/max(vdist.z, vdist.w));
170 | gl_FragColor.a = 1.0-smoothstep(1.0-dt, 1.0, dist);
171 | }
172 |
173 | // respond to touch with radial
174 | const float dur = 200.0;
175 | const float clr = 0.035;
176 |
177 | float since = vtouch.w;
178 | vec4 react = vec4(0.0);
179 |
180 | // float d = length(vtouch.xy-vdist.xy);
181 | // float d = length(vtouch.xx-vdist.xx); // horizontal sweep
182 |
183 | const float magic = 100.0;
184 | float d = length((vtouch.xy*vdist.zw)-(vdist.xy*vdist.zw));
185 | d /= magic;
186 |
187 | float t = since/dur;
188 | if (d < 2.0*t) {
189 | if (t < sqrt2) { // color in
190 | react = vec4(clr);
191 | } else if (t < pi) { // fade out
192 | float fac = 1.0 - (t-sqrt2)/(pi-sqrt2);
193 | react = vec4(fac*clr);
194 | }
195 | }
196 |
197 | gl_FragColor += react;
198 | } else {
199 | discard;
200 | }
201 | }
202 | }
203 |
--------------------------------------------------------------------------------
/example/boxes/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "log"
6 | "time"
7 |
8 | "dasa.cc/material"
9 | "dasa.cc/signal"
10 | "golang.org/x/mobile/app"
11 | "golang.org/x/mobile/event/lifecycle"
12 | "golang.org/x/mobile/event/paint"
13 | "golang.org/x/mobile/event/size"
14 | "golang.org/x/mobile/event/touch"
15 | "golang.org/x/mobile/exp/f32"
16 | "golang.org/x/mobile/gl"
17 | )
18 |
19 | var (
20 | env = new(material.Environment)
21 | boxes [9]*material.Material
22 | sig signal.Discrete
23 | quits []chan struct{}
24 | )
25 |
26 | func onStart(ctx gl.Context) {
27 | env.SetPalette(material.Palette{
28 | Primary: material.BlueGrey500,
29 | Dark: material.BlueGrey700,
30 | Light: material.BlueGrey100,
31 | Accent: material.DeepOrangeA200,
32 | })
33 |
34 | quits = []chan struct{}{}
35 |
36 | sig = make(signal.Discrete, len(material.ExpSig))
37 | copy(sig, material.ExpSig)
38 | rsig := make(signal.Discrete, len(material.ExpSig))
39 | copy(rsig, material.ExpSig)
40 | rsig.UnitInverse()
41 | sig = append(sig, rsig...)
42 | sig.NormalizeRange(0, 1)
43 |
44 | ctx.Enable(gl.BLEND)
45 | ctx.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
46 |
47 | ctx.Enable(gl.CULL_FACE)
48 | ctx.CullFace(gl.BACK)
49 |
50 | env.Load(ctx)
51 | env.LoadGlyphs(ctx)
52 |
53 | for i := range boxes {
54 | boxes[i] = env.NewMaterial(ctx)
55 | boxes[i].SetColor(material.BlueGrey200)
56 | }
57 | }
58 |
59 | func onStop(ctx gl.Context) {
60 | env.Unload(ctx)
61 | for i := range boxes {
62 | boxes[i] = nil
63 | }
64 | for _, q := range quits {
65 | q <- struct{}{}
66 | }
67 | }
68 |
69 | func onLayout(sz size.Event) {
70 | env.SetOrtho(sz)
71 | // env.SetPerspective(sz)
72 | env.StartLayout()
73 |
74 | for i, box := range boxes {
75 | env.AddConstraints(box.Width(100), box.Height(100), box.Z(float32(i+1)))
76 | }
77 |
78 | b, p := env.Box, env.Grid.Gutter
79 | env.AddConstraints(
80 | boxes[0].StartIn(b, p), boxes[0].TopIn(b, p),
81 | boxes[1].CenterHorizontalIn(b), boxes[1].TopIn(b, p),
82 | boxes[2].EndIn(b, p), boxes[2].TopIn(b, p),
83 | boxes[3].CenterVerticalIn(b), boxes[3].StartIn(b, p),
84 | boxes[4].CenterVerticalIn(b), boxes[4].CenterHorizontalIn(b),
85 | boxes[5].CenterVerticalIn(b), boxes[5].EndIn(b, p),
86 | boxes[6].StartIn(b, p), boxes[6].BottomIn(b, p),
87 | boxes[7].CenterHorizontalIn(b), boxes[7].BottomIn(b, p),
88 | boxes[8].EndIn(b, p), boxes[8].BottomIn(b, p),
89 | )
90 |
91 | boxes[4].SetTextColor(material.Black)
92 | boxes[4].SetTextHeight(material.Dp(24).Px())
93 |
94 | for _, q := range quits {
95 | q <- struct{}{}
96 | }
97 | quits = quits[:0]
98 |
99 | log.Println("starting layout")
100 | t := time.Now()
101 | env.FinishLayout()
102 | log.Printf("finished layout in %s\n", time.Now().Sub(t))
103 |
104 | func() {
105 | m := boxes[1].World()
106 | x, z := m[0][3], m[2][3]
107 | quits = append(quits, material.Animation{
108 | Sig: sig,
109 | Dur: 2000 * time.Millisecond,
110 | Loop: true,
111 | Interp: func(dt float32) {
112 | m[0][3] = x + 200*dt
113 | },
114 | }.Do())
115 | quits = append(quits, material.Animation{
116 | Sig: sig,
117 | Dur: 1000 * time.Millisecond,
118 | Loop: true,
119 | Interp: func(dt float32) {
120 | m[2][3] = z + 4*dt
121 | },
122 | }.Do())
123 | }()
124 |
125 | func() {
126 | m := boxes[2].World()
127 | z := m[2][3]
128 | quits = append(quits, material.Animation{
129 | Sig: sig,
130 | Dur: 2000 * time.Millisecond,
131 | Loop: true,
132 | Interp: func(dt float32) {
133 | m[2][3] = z + 10*dt
134 | },
135 | }.Do())
136 | }()
137 |
138 | func() {
139 | m := boxes[4].World()
140 | x, y := m[0][3], m[1][3]
141 | w, h := m[0][0], m[1][1]
142 | quits = append(quits, material.Animation{
143 | Sig: sig,
144 | Dur: 4000 * time.Millisecond,
145 | Loop: true,
146 | Interp: func(dt float32) {
147 | m[0][0] = w + 200*dt
148 | m[0][3] = x - 200*dt/2
149 | boxes[4].SetText(fmt.Sprintf("w: %.2f\nh: %.2f", m[0][0], m[1][1]))
150 | },
151 | }.Do())
152 | quits = append(quits, material.Animation{
153 | Sig: sig,
154 | Dur: 2000 * time.Millisecond,
155 | Loop: true,
156 | Interp: func(dt float32) {
157 | m[1][1] = h + 200*dt
158 | m[1][3] = y - 200*dt/2
159 | },
160 | }.Do())
161 | }()
162 |
163 | func() {
164 | m := boxes[6].World()
165 | w, h := m[0][0], m[1][1]
166 | z := m[2][3]
167 | quits = append(quits, material.Animation{
168 | Sig: sig,
169 | Dur: 4000 * time.Millisecond,
170 | Loop: true,
171 | Interp: func(dt float32) {
172 | boxes[6].Roundness = 50 * (1 - dt)
173 | m[0][0] = w + 200*dt
174 | m[1][1] = h + 200*dt
175 | },
176 | }.Do())
177 | quits = append(quits, material.Animation{
178 | Sig: sig,
179 | Dur: 8000 * time.Millisecond,
180 | Loop: true,
181 | Interp: func(dt float32) {
182 | m[2][3] = z + 7*dt
183 | },
184 | }.Do())
185 | }()
186 |
187 | func() {
188 | m := boxes[8].World()
189 | w := m[0][0]
190 | quits = append(quits, material.Animation{
191 | Sig: sig,
192 | Dur: 2000 * time.Millisecond,
193 | Loop: true,
194 | Interp: func(dt float32) {
195 | boxes[8].Roundness = (w / 2) * dt
196 | },
197 | }.Do())
198 | }()
199 |
200 | _ = f32.Vec3{}
201 | // env.View.Translate(&env.View, 400, 250, 400)
202 | // env.View.Rotate(&env.View, 0.785, &f32.Vec3{0, 0, 1})
203 | // env.View.Rotate(&env.View, 0.785, &f32.Vec3{0, 1, 0})
204 | }
205 |
206 | var lastpaint time.Time
207 | var fps int
208 |
209 | func onPaint(ctx gl.Context) {
210 | ctx.ClearColor(material.BlueGrey100.RGBA())
211 | ctx.Clear(gl.COLOR_BUFFER_BIT)
212 | env.Draw(ctx)
213 | now := time.Now()
214 | fps = int(time.Second / now.Sub(lastpaint))
215 | lastpaint = now
216 | }
217 |
218 | func main() {
219 | app.Main(func(a app.App) {
220 | var glctx gl.Context
221 | var ticker *time.Ticker
222 | for ev := range a.Events() {
223 | switch ev := a.Filter(ev).(type) {
224 | case lifecycle.Event:
225 | switch ev.Crosses(lifecycle.StageVisible) {
226 | case lifecycle.CrossOn:
227 | if ticker != nil {
228 | ticker.Stop()
229 | }
230 | ticker = time.NewTicker(time.Second)
231 | go func() {
232 | for range ticker.C {
233 | log.Printf("fps=%-4v\n", fps)
234 | }
235 | }()
236 | glctx = ev.DrawContext.(gl.Context)
237 | onStart(glctx)
238 | a.Send(paint.Event{})
239 | case lifecycle.CrossOff:
240 | if ticker != nil {
241 | ticker.Stop()
242 | }
243 | onStop(glctx)
244 | glctx = nil
245 | }
246 | case size.Event:
247 | if glctx == nil {
248 | a.Send(ev) // republish event until onStart is called
249 | } else {
250 | onLayout(ev)
251 | }
252 | case paint.Event:
253 | if glctx == nil || ev.External {
254 | continue
255 | }
256 | onPaint(glctx)
257 | a.Publish()
258 | a.Send(paint.Event{})
259 | case touch.Event:
260 | env.Touch(ev)
261 | }
262 | }
263 | })
264 | }
265 |
--------------------------------------------------------------------------------
/assets/glsl.go:
--------------------------------------------------------------------------------
1 | // generated by gen.go; DO NOT EDIT
2 | package assets
3 |
4 | var VertexShader = `#version 100
5 |
6 | attribute vec4 vertex;
7 | attribute vec4 color;
8 |
9 | // sample with xy, offset at zw
10 | attribute vec4 texcoord;
11 |
12 | // xy is relative position of originating touch event
13 | // z is state of touch event; begin (0), move (1), end (2)
14 | // w is timing information
15 | attribute vec4 touch;
16 |
17 | // v0, v1 == 1 && v2, v3 == 0, interpolated in fragment shader
18 | // to determine location since all materials are drawn as single
19 | // mesh.
20 | attribute vec4 dist;
21 |
22 | // uniform mat4 world;
23 | uniform mat4 view;
24 | uniform mat4 proj;
25 |
26 | varying vec4 vcolor;
27 | varying vec4 vtexcoord;
28 | varying vec4 vdist;
29 | varying vec4 vvertex;
30 | varying vec4 vtouch;
31 |
32 | void main() {
33 | // TODO review if fragment shader *really* needs access to z coord
34 | // of shadow's material.
35 | vec4 vert = vec4(vertex.xyz, 1.0);
36 | if (vert.z < 0.0) {
37 | //vert.z = 0.0;
38 | }
39 | //gl_Position = vert * view * proj;
40 | //gl_Position = proj * view * vert;
41 | gl_Position = proj * view * vert;
42 | vcolor = color;
43 | vtexcoord = texcoord;
44 | vvertex = vertex;
45 | vdist = dist;
46 | vtouch = touch;
47 | }
48 | `
49 |
50 | var FragmentShader = `#version 100
51 | #define onesqrt2 0.70710678118
52 | #define sqrt2 1.41421356237
53 | #define pi 3.14159265359
54 | #define twopi 6.28318530718
55 |
56 | #define touchBegin 0.0
57 | #define touchMove 1.0
58 | #define touchEnd 2.0
59 | precision mediump float;
60 |
61 | // TODO pass this in some other way so sampler can be selected
62 | // 0:fontsize, 1:pad, 2:edge
63 | uniform vec4 glyphconf;
64 |
65 | uniform sampler2D image;
66 |
67 | uniform sampler2D texglyph;
68 | uniform sampler2D texicon;
69 | uniform vec2 glyph;
70 | uniform vec4 shadowColor;
71 |
72 | varying vec4 vtexcoord;
73 | varying vec4 vvertex;
74 | varying vec4 vtouch;
75 |
76 | // interpolated distance, and size values
77 | // x, y is unit value [0..1]
78 | // z, w is material width, height
79 | varying vec4 vdist;
80 | varying vec4 vcolor;
81 |
82 | vec4 sampleIcon() {
83 | vec4 clr = texture2D(texicon, vtexcoord.xy);
84 | clr.rgb += vcolor.rgb;
85 | clr.a *= 0.54; // https://www.google.com/design/spec/style/color.html#color-ui-color-application
86 | return clr;
87 | }
88 |
89 | vec4 sampleImage() {
90 | vec4 clr = texture2D(image, vtexcoord.xy);
91 | return clr;
92 | }
93 |
94 | // four values for a texture might look like this
95 | // [
96 | // morton-encoded bounds,
97 | // texture size,
98 | // ?type? so as to invoke specific shader bits (glyph, icon, image)
99 | // alpha
100 | // ]
101 | //
102 | // numbers are 32-bit
103 | // must have four values to id rectangle for texture coordinates.
104 | // could reduce this to 3 numbers if two were uint16 containers and one
105 | // specified texture size. If texture size could be set only once then
106 | // it could be two. Perhaps set texture size in a uniform? that would
107 | // allow for 2 numbers to specify rectangle and 1 for type and 1 for alpha.
108 |
109 | vec4 sampleGlyph() {
110 | float fontsize = glyphconf.x;
111 | float pad = glyphconf.y;
112 | float edge = glyphconf.z;
113 |
114 | float d = texture2D(texglyph, vtexcoord.xy).a;
115 | float h = vdist.w;
116 | float gamma = 0.22/(pad*(h/fontsize));
117 |
118 | // d += 0.2; // bold
119 | // d -= 0.2; // thin
120 |
121 | vec4 clr = vcolor;
122 | clr.a = smoothstep(edge-gamma, edge+gamma, d);
123 | clr.a *= 0.87; // secondary text
124 | return clr;
125 | }
126 |
127 | // TODO drop this
128 | bool shouldcolor(vec2 pos, float sz) {
129 | pos = 0.5-abs(pos-0.5);
130 | pos *= vdist.zw;
131 |
132 | if (pos.x <= sz && pos.y <= sz) {
133 | float d = length(1.0-(pos/sz));
134 | if (d > 1.0) {
135 | return false;
136 | }
137 | }
138 | return true;
139 | }
140 |
141 | float shade(vec2 pos, float sz) {
142 | pos = 0.5-abs(pos-0.5);
143 | pos *= vdist.zw;
144 |
145 | if (pos.x <= sz && pos.y <= sz) {
146 | float d = length(1.0-(pos/sz));
147 | if (d > 1.0) { // TODO consider moving this as discard into top of main
148 | return 0.0;
149 | }
150 | return 1.0-d;
151 | } else if (pos.x <= sz && pos.y > sz) {
152 | return pos.x/sz;
153 | } else if (pos.x > sz && pos.y <= sz) {
154 | return pos.y/sz;
155 | }
156 | return 1.0;
157 | }
158 |
159 | void main() {
160 | float roundness = vvertex.w;
161 |
162 | if (vtexcoord.x >= 0.0) {
163 | if (vtexcoord.z == 3.0) {
164 | gl_FragColor = sampleImage();
165 | } else if (vtexcoord.z == 1.0) {
166 | gl_FragColor = sampleIcon();
167 | } else {
168 | gl_FragColor = sampleGlyph();
169 | }
170 | } else if (vvertex.z <= 0.0) { // draw shadow
171 | if (roundness < 8.0) {
172 | roundness = 8.0;
173 | }
174 |
175 | // TODO ellipsis are being over-rounded resulting in a distortion
176 | // in the shadow. The distortion isn't very noticable unless drawing
177 | // only shadows, but this does affect outline of material making it
178 | // more difficult to see material edges. Clamping roundness results
179 | // in a strange distortion when animating roundness and the cause is
180 | // not currently clear.
181 | //
182 | // At the same time, the distortion looks better for different cases.
183 | roundness += -vvertex.z;
184 | // if (roundness > vdist.z/2.0) {
185 | // roundness = vdist.z/2.0;
186 | // }
187 |
188 | gl_FragColor = shadowColor;
189 |
190 | // maps roundness to 1 - [0..0.66] and helps shadows cast by rectangles and ellipses
191 | // look similar at the same z index.
192 | // TODO vvertex.w is roundness, double check usage here as was used before float roundness declared
193 | float e = 1.0 - (vvertex.w/vdist.z/0.75);
194 |
195 | gl_FragColor.a = smoothstep(0.0, e, shade(vdist.xy, roundness));
196 | vec2 n = abs(vdist.xy*2.0 - 1.0);
197 | n = n*n*n*n;
198 | n = 1.0-n;
199 |
200 | // reduce alpha/strength as z-index increases
201 | float f = 1.0 + (-vvertex.z*0.1);
202 |
203 | gl_FragColor.a *= n.x*n.y/f;
204 | // gl_FragColor.a *= 10.0;
205 | } else { // draw material
206 | if (shouldcolor(vdist.xy, roundness)) {
207 | gl_FragColor = vcolor;
208 |
209 | // anti-alias roundness
210 | if (gl_FragColor.a != 0.0) { // guard against exposing bg of text and icon content
211 | float dist = 1.0-shade(vdist.xy, roundness);
212 | // fractional based on largest size, approximates a consistent value across resolutions
213 | float dt = (5.0/max(vdist.z, vdist.w));
214 | gl_FragColor.a = 1.0-smoothstep(1.0-dt, 1.0, dist);
215 | }
216 |
217 | // respond to touch with radial
218 | const float dur = 200.0;
219 | const float clr = 0.035;
220 |
221 | float since = vtouch.w;
222 | vec4 react = vec4(0.0);
223 |
224 | // float d = length(vtouch.xy-vdist.xy);
225 | // float d = length(vtouch.xx-vdist.xx); // horizontal sweep
226 |
227 | const float magic = 100.0;
228 | float d = length((vtouch.xy*vdist.zw)-(vdist.xy*vdist.zw));
229 | d /= magic;
230 |
231 | float t = since/dur;
232 | if (d < 2.0*t) {
233 | if (t < sqrt2) { // color in
234 | react = vec4(clr);
235 | } else if (t < pi) { // fade out
236 | float fac = 1.0 - (t-sqrt2)/(pi-sqrt2);
237 | react = vec4(fac*clr);
238 | }
239 | }
240 |
241 | gl_FragColor += react;
242 | } else {
243 | discard;
244 | }
245 | }
246 | }
247 | `
248 |
--------------------------------------------------------------------------------
/material.go:
--------------------------------------------------------------------------------
1 | package material
2 |
3 | import (
4 | "time"
5 |
6 | "dasa.cc/material/glutil"
7 | "dasa.cc/material/icon"
8 | "dasa.cc/simplex"
9 | "golang.org/x/mobile/event/touch"
10 | "golang.org/x/mobile/exp/f32"
11 | "golang.org/x/mobile/gl"
12 | )
13 |
14 | var (
15 | DefaultFilter = glutil.TextureFilter(gl.LINEAR, gl.LINEAR)
16 | DefaultWrap = glutil.TextureWrap(gl.REPEAT, gl.REPEAT)
17 |
18 | linearFilter = glutil.TextureFilter(gl.LINEAR, gl.LINEAR)
19 | nearestFilter = glutil.TextureFilter(gl.NEAREST, gl.NEAREST)
20 | glyphsFilter = glutil.TextureFilter(gl.LINEAR_MIPMAP_LINEAR, gl.LINEAR)
21 | )
22 |
23 | type Material struct {
24 | Box
25 |
26 | Drawer glutil.DrawerFunc
27 |
28 | col4, col8, col12 int
29 |
30 | hidden bool
31 |
32 | BehaviorFlags Behavior
33 |
34 | text struct {
35 | value string
36 | height float32
37 | r, g, b, a float32
38 | }
39 |
40 | icon struct {
41 | x, y float32
42 | r, g, b, a float32
43 | }
44 |
45 | cr, cg, cb, ca float32 // color for uniform
46 |
47 | IsCircle bool
48 | Roundness float32
49 |
50 | touch struct {
51 | state touch.Type
52 | x, y float32
53 | start time.Time
54 | }
55 |
56 | ShowImage bool
57 | Rotate float32 // Radian
58 | }
59 |
60 | func (mtrl *Material) Span(col4, col8, col12 int) {
61 | mtrl.col4, mtrl.col8, mtrl.col12 = col4, col8, col12
62 | }
63 |
64 | func New(ctx gl.Context, color Color) *Material {
65 | mtrl := &Material{
66 | BehaviorFlags: DescriptorRaised,
67 | }
68 | mtrl.icon.x, mtrl.icon.y = -1, -1
69 | mtrl.touch.state = touch.TypeEnd
70 | mtrl.cr, mtrl.cg, mtrl.cb, mtrl.ca = color.RGBA()
71 |
72 | return mtrl
73 | }
74 |
75 | // SetColor sets background color of material unless material flags contains DescriptorFlat.
76 | func (mtrl *Material) SetColor(color Color) {
77 | mtrl.cr, mtrl.cg, mtrl.cb, mtrl.ca = color.RGBA()
78 | }
79 |
80 | func (mtrl *Material) SetIcon(ic icon.Icon) {
81 | mtrl.icon.x, mtrl.icon.y = ic.Texcoords()
82 | }
83 |
84 | func (mtrl *Material) SetIconColor(color Color) {
85 | mtrl.icon.r, mtrl.icon.g, mtrl.icon.b, mtrl.icon.a = color.RGBA()
86 | }
87 |
88 | func (mtrl *Material) SetTextColor(color Color) {
89 | mtrl.text.r, mtrl.text.g, mtrl.text.b, mtrl.text.a = color.RGBA()
90 | }
91 |
92 | func (mtrl *Material) SetTextHeight(h float32) {
93 | mtrl.text.height = h
94 | }
95 |
96 | func (mtrl *Material) SetText(s string) {
97 | mtrl.text.value = s
98 | }
99 |
100 | func (mtrl *Material) Bind(lpro *simplex.Program) {
101 | mtrl.Box = NewBox(lpro)
102 | }
103 |
104 | func (mtrl *Material) World() *f32.Mat4 { return &mtrl.world }
105 |
106 | func (mtrl *Material) Hidden() bool { return mtrl.hidden }
107 |
108 | func (mtrl *Material) M() *Material { return mtrl }
109 |
110 | func (mtrl *Material) Contains(tx, ty float32) bool {
111 | x, y, w, h := mtrl.world[0][3], mtrl.world[1][3], mtrl.world[0][0], mtrl.world[1][1]
112 | return x <= tx && tx <= x+w && y <= ty && ty <= y+h
113 | }
114 |
115 | func (mtrl *Material) RelativeCoords(tx, ty float32) (float32, float32) {
116 | x, y, w, h := mtrl.world[0][3], mtrl.world[1][3], mtrl.world[0][0], mtrl.world[1][1]
117 | return (tx - x) / w, (ty - y) / h
118 | }
119 |
120 | func (mtrl *Material) Constraints(env *Environment) []simplex.Constraint {
121 | return nil
122 | }
123 |
124 | // TODO seems to slow down goimport ...
125 | var shdr, shdg, shdb, shda = BlueGrey900.RGBA()
126 |
127 | type Button struct {
128 | *Material
129 | OnPress func()
130 | OnTouch func(touch.Event)
131 | }
132 |
133 | type FloatingActionButton struct {
134 | *Material
135 | Mini bool
136 | OnPress func()
137 | OnTouch func(touch.Event)
138 | }
139 |
140 | func (fab *FloatingActionButton) Constraints(env *Environment) []simplex.Constraint {
141 | var size float32
142 | switch env.Grid.Columns {
143 | case 4, 8:
144 | if fab.Mini {
145 | size = Dp(40).Px()
146 | } else {
147 | size = Dp(56).Px()
148 | }
149 | case 12:
150 | if fab.Mini {
151 | size = Dp(48).Px() // TODO size unconfirmed
152 | } else {
153 | size = Dp(64).Px()
154 | }
155 | }
156 | fab.Roundness = size / 2 // TODO consider how this should work
157 | return []simplex.Constraint{fab.Width(size), fab.Height(size), fab.Z(6)}
158 | }
159 |
160 | // TODO https://www.google.com/design/spec/layout/structure.html#structure-toolbars
161 | type Toolbar struct {
162 | *Material
163 | Nav *Button
164 | Title *Material
165 | actions []*Button
166 | }
167 |
168 | func (bar *Toolbar) AddAction(btn *Button) {
169 | btn.BehaviorFlags = DescriptorFlat
170 | btn.SetIconColor(Black)
171 | bar.actions = append(bar.actions, btn)
172 | }
173 |
174 | func (tb *Toolbar) Constraints(env *Environment) []simplex.Constraint {
175 | stp := env.Grid.StepSize()
176 | var (
177 | width, height float32
178 | btnsize float32
179 | titleStart float32
180 | )
181 |
182 | switch env.Grid.Columns {
183 | case 4:
184 | width = float32(tb.col4) * stp
185 | height = Dp(56).Px()
186 | btnsize = Dp(24).Px()
187 | titleStart = Dp(48).Px()
188 | case 8:
189 | width = float32(tb.col8) * stp
190 | height = Dp(56).Px()
191 | btnsize = Dp(24).Px()
192 | titleStart = Dp(72).Px()
193 | case 12:
194 | width = float32(tb.col12) * stp
195 | height = Dp(64).Px()
196 | btnsize = Dp(32).Px()
197 | titleStart = Dp(72).Px()
198 | }
199 | nav := tb.Nav
200 | title := tb.Title
201 | cns := []simplex.Constraint{
202 | tb.Width(width), tb.Height(height), tb.Z(4),
203 | tb.StartIn(env.Box, env.Grid.Margin), tb.TopIn(env.Box, env.Grid.Margin),
204 | nav.Width(btnsize), nav.Height(btnsize), nav.Z(5),
205 | nav.StartIn(tb.Box, env.Grid.Gutter),
206 | nav.CenterVerticalIn(tb.Box),
207 | title.StartIn(tb.Box, titleStart), title.Before(tb.actions[len(tb.actions)-1].Box, 0),
208 | title.CenterVerticalIn(tb.Box), title.Height(btnsize), title.Z(5),
209 | }
210 |
211 | for i, btn := range tb.actions {
212 | cns = append(cns, btn.Width(btnsize), btn.Height(btnsize), btn.Z(5), btn.CenterVerticalIn(tb.Box))
213 | if i == 0 {
214 | cns = append(cns, btn.EndIn(tb.Box, env.Grid.Gutter))
215 | } else {
216 | cns = append(cns, btn.Before(tb.actions[i-1].Box, env.Grid.Gutter))
217 | }
218 | }
219 |
220 | return cns
221 | }
222 |
223 | type NavDrawer struct {
224 | *Material
225 | }
226 |
227 | type Menu struct {
228 | *Material
229 | selected int
230 | actions []*Button
231 | }
232 |
233 | func (mu *Menu) AddAction(btn *Button) {
234 | btn.BehaviorFlags = DescriptorFlat
235 | btn.SetIconColor(Black)
236 | btn.hidden = mu.hidden
237 | mu.actions = append(mu.actions, btn)
238 | }
239 |
240 | func (mu *Menu) ShowAt(m *f32.Mat4) {
241 | x := mu.Box.world[0][3]
242 | y := mu.Box.world[1][3]
243 | mu.Box.world[0][3] = m[0][3]
244 | mu.Box.world[1][3] = m[1][3] + m[1][1] - mu.Box.world[1][1]
245 | dx := mu.Box.world[0][3] - x
246 | dy := mu.Box.world[1][3] - y
247 | for _, btn := range mu.actions {
248 | btn.Box.world[0][3] += dx
249 | btn.Box.world[1][3] += dy
250 | }
251 | mu.Show()
252 | }
253 |
254 | func (mu *Menu) Show() {
255 | go func() {
256 | h := mu.Box.world[1][1]
257 | y := mu.Box.world[1][3]
258 | anim := Animation{
259 | Sig: ExpSig,
260 | Dur: 300 * time.Millisecond,
261 | Start: func() {
262 | mu.Box.world[1][1] = 0
263 | mu.Box.world[1][3] = y + h
264 | mu.hidden = false
265 | for _, btn := range mu.actions {
266 | btn.hidden = false
267 | }
268 | },
269 | Interp: func(dt float32) {
270 | mu.Box.world[1][1] = h * dt
271 | mu.Box.world[1][3] = (y + h) - h*dt
272 | },
273 | End: func() {
274 | mu.Box.world[1][1] = h
275 | mu.Box.world[1][3] = y
276 | },
277 | }
278 | anim.Do()
279 | }()
280 | }
281 |
282 | func (mu *Menu) Hide() {
283 | h := mu.Box.world[1][1]
284 | y := mu.Box.world[1][3]
285 | Animation{
286 | Sig: ExpSig,
287 | Dur: 200 * time.Millisecond,
288 | Interp: func(dt float32) {
289 | mu.Box.world[1][1] = h * (1 - dt)
290 | mu.Box.world[1][3] = (y + h) - h*(1-dt)
291 | },
292 | End: func() {
293 | mu.hidden = true
294 | for _, btn := range mu.actions {
295 | btn.hidden = true
296 | }
297 | mu.Box.world[1][1] = h
298 | mu.Box.world[1][3] = y
299 | },
300 | }.Do()
301 | }
302 |
303 | func (mu *Menu) Constraints(env *Environment) []simplex.Constraint {
304 | cns := []simplex.Constraint{
305 | mu.Width(Dp(100).Px()), mu.Z(8),
306 | mu.StartIn(env.Box, env.Grid.Margin), mu.TopIn(env.Box, env.Grid.Margin),
307 | }
308 |
309 | for i, btn := range mu.actions {
310 | cns = append(cns, btn.Width(Dp(100).Px()), btn.Height(Dp(16).Px()), btn.Z(9), btn.StartIn(mu.Box, Dp(16).Px()))
311 | if i == 0 {
312 | cns = append(cns, btn.TopIn(mu.Box, env.Grid.Gutter))
313 | } else {
314 | cns = append(cns, btn.Below(mu.actions[i-1].Box, Dp(20).Px()))
315 | }
316 | }
317 | cns = append(cns, mu.AlignBottoms(mu.actions[len(mu.actions)-1].Box, Dp(20).Px()))
318 |
319 | return cns
320 | }
321 |
--------------------------------------------------------------------------------
/glutil/gl.go:
--------------------------------------------------------------------------------
1 | package glutil
2 |
3 | import (
4 | "io/ioutil"
5 | "log"
6 | "math"
7 |
8 | "golang.org/x/mobile/asset"
9 | "golang.org/x/mobile/exp/f32"
10 | "golang.org/x/mobile/gl"
11 | )
12 |
13 | func MustOpen(name string) asset.File {
14 | f, err := asset.Open(name)
15 | if err != nil {
16 | log.Fatal(err)
17 | }
18 | return f
19 | }
20 |
21 | func MustReadAll(name string) []byte {
22 | f := MustOpen(name)
23 | b, err := ioutil.ReadAll(f)
24 | if err != nil {
25 | log.Fatal(err)
26 | }
27 | return b
28 | }
29 |
30 | func ShaderAsset(typ gl.Enum, name string) func(gl.Context) gl.Shader {
31 | return ShaderCompile(typ, name, string(MustReadAll(name)))
32 | }
33 |
34 | func VertAsset(name string) func(gl.Context) gl.Shader {
35 | return ShaderAsset(gl.VERTEX_SHADER, name)
36 | }
37 |
38 | func FragAsset(name string) func(gl.Context) gl.Shader {
39 | return ShaderAsset(gl.FRAGMENT_SHADER, name)
40 | }
41 |
42 | func ShaderCompile(typ gl.Enum, name string, src string) func(gl.Context) gl.Shader {
43 | return func(ctx gl.Context) gl.Shader {
44 | s := ctx.CreateShader(typ)
45 | ctx.ShaderSource(s, src)
46 | ctx.CompileShader(s)
47 | if ctx.GetShaderi(s, gl.COMPILE_STATUS) == 0 {
48 | log.Fatalf("glutil %s: %s\n", name, ctx.GetShaderInfoLog(s))
49 | }
50 | return s
51 | }
52 | }
53 |
54 | // TODO what if Program could use reflection and map functions?
55 | // really, it doesn't need to do functions at all! just iter over
56 | // attribs and uniforms and match
57 | // TODO use reflection to set values
58 | // type TexProgram struct {
59 | // Program
60 | // setworld func(f32.Mat4)
61 | // setsampler func(int)
62 | // world gl.Uniform
63 | // view gl.Uniform
64 | // matrix f32.Mat4 `glsl:"gl.Uniform"` // not really necessary, could just fail if same name exists in vert and frag shader
65 | // sampler gl.Uniform
66 | // position gl.Attrib
67 | // texcoord gl.Attrib
68 | // }
69 | type Program struct{ gl.Program }
70 |
71 | func (prg *Program) CreateAndLink(ctx gl.Context, compilers ...func(gl.Context) gl.Shader) {
72 | prg.Program = ctx.CreateProgram()
73 | for _, c := range compilers {
74 | s := c(ctx)
75 | ctx.AttachShader(prg.Program, s)
76 | defer ctx.DeleteShader(s)
77 | }
78 | ctx.LinkProgram(prg.Program)
79 | if ctx.GetProgrami(prg.Program, gl.LINK_STATUS) == 0 {
80 | log.Fatalf("program link: %s", ctx.GetProgramInfoLog(prg.Program))
81 | }
82 | }
83 |
84 | func (prg Program) Delete(ctx gl.Context) {
85 | ctx.DeleteProgram(prg.Program)
86 | }
87 |
88 | func (prg Program) Uniform(ctx gl.Context, name string) gl.Uniform {
89 | return ctx.GetUniformLocation(prg.Program, name)
90 | }
91 |
92 | func (prg Program) Attrib(ctx gl.Context, name string) gl.Attrib {
93 | return ctx.GetAttribLocation(prg.Program, name)
94 | }
95 |
96 | func (prg Program) Use(ctx gl.Context, options ...func(gl.Context, Program)) {
97 | ctx.UseProgram(prg.Program)
98 | for _, opt := range options {
99 | opt(ctx, prg)
100 | }
101 | }
102 |
103 | func (prg Program) Mat4(ctx gl.Context, dst gl.Uniform, src f32.Mat4) {
104 | ctx.UniformMatrix4fv(dst, []float32{
105 | src[0][0], src[1][0], src[2][0], src[3][0],
106 | src[0][1], src[1][1], src[2][1], src[3][1],
107 | src[0][2], src[1][2], src[2][2], src[3][2],
108 | src[0][3], src[1][3], src[2][3], src[3][3],
109 | })
110 | }
111 |
112 | func (prg Program) U1i(ctx gl.Context, dst gl.Uniform, v int) {
113 | ctx.Uniform1i(dst, v)
114 | }
115 |
116 | func (prg Program) U2i(ctx gl.Context, dst gl.Uniform, v0, v1 int) {
117 | ctx.Uniform2i(dst, v0, v1)
118 | }
119 |
120 | func (prg Program) U1f(ctx gl.Context, dst gl.Uniform, v float32) {
121 | ctx.Uniform1f(dst, v)
122 | }
123 |
124 | func (prg Program) U2f(ctx gl.Context, dst gl.Uniform, v0, v1 float32) {
125 | ctx.Uniform2f(dst, v0, v1)
126 | }
127 |
128 | func (prg Program) U4f(ctx gl.Context, dst gl.Uniform, v0, v1, v2, v3 float32) {
129 | ctx.Uniform4f(dst, v0, v1, v2, v3)
130 | }
131 |
132 | // TODO an Attrib type that describes it's format would be useful here
133 | func (prg Program) Pointer(ctx gl.Context, a gl.Attrib, size int) {
134 | ctx.EnableVertexAttribArray(a)
135 | ctx.VertexAttribPointer(a, size, gl.FLOAT, false, 0, 0)
136 | }
137 |
138 | // func UniformMat4(dst gl.Uniform) func(gl.Context, f32.Mat4) {
139 | // src := make([]float32, 16)
140 | // return func(ctx gl.Context, m f32.Mat4) {
141 | // for i, v := range m {
142 | // for j, x := range v {
143 | // src[i*4+j] = x
144 | // }
145 | // }
146 | // ctx.UniformMatrix4fv(dst, src)
147 | // }
148 | // }
149 |
150 | // TODO want this ???
151 | type Uniform4fFunc func(gl.Context, f32.Vec4)
152 |
153 | func UniformVec4(dst gl.Uniform) func(gl.Context, f32.Vec4) {
154 | return func(ctx gl.Context, v f32.Vec4) { ctx.Uniform4f(dst, v[0], v[1], v[2], v[3]) }
155 | }
156 |
157 | func UniformFloat(dst gl.Uniform) func(gl.Context, float32) {
158 | return func(ctx gl.Context, x float32) { ctx.Uniform1f(dst, x) }
159 | }
160 |
161 | type Buffer struct {
162 | gl.Buffer
163 | target gl.Enum
164 | }
165 |
166 | func (buf *Buffer) Create(ctx gl.Context, target gl.Enum) {
167 | buf.Buffer = ctx.CreateBuffer()
168 | // buf.Update = BufferFloatData(target, usage)
169 | buf.target = target
170 | }
171 |
172 | func (buf Buffer) Delete(ctx gl.Context) {
173 | ctx.DeleteBuffer(buf.Buffer)
174 | // buf.Update = nil
175 | }
176 |
177 | func (buf Buffer) Bind(ctx gl.Context, after ...func(gl.Context)) {
178 | ctx.BindBuffer(buf.target, buf.Buffer)
179 | for _, fn := range after {
180 | fn(ctx)
181 | }
182 | }
183 |
184 | func (buf Buffer) Draw(ctx gl.Context, mode gl.Enum, first int, count int, before ...func(gl.Context)) {
185 | buf.Bind(ctx, before...)
186 | ctx.DrawArrays(mode, first, count)
187 | }
188 |
189 | func (buf Buffer) DrawElements(ctx gl.Context, mode gl.Enum, count int, typ gl.Enum, offset int, before ...func(gl.Context)) {
190 | buf.Bind(ctx, before...)
191 | ctx.DrawElements(mode, count, typ, offset)
192 | }
193 |
194 | func VertexAttrib(a gl.Attrib, size int, typ gl.Enum, normalize bool, stride int, offset int) func(gl.Context) {
195 | return func(ctx gl.Context) {
196 | ctx.EnableVertexAttribArray(a)
197 | ctx.VertexAttribPointer(a, size, typ, normalize, stride, offset)
198 | }
199 | }
200 |
201 | func BufferFloatData(target, usage gl.Enum) func(gl.Context, []float32) {
202 | var bin []byte
203 | return func(ctx gl.Context, data []float32) {
204 | subok := len(bin) > 0 && len(data)*4 <= len(bin)
205 | if !subok {
206 | bin = make([]byte, len(data)*4)
207 | }
208 | for i, x := range data {
209 | u := math.Float32bits(x)
210 | bin[4*i+0] = byte(u >> 0)
211 | bin[4*i+1] = byte(u >> 8)
212 | bin[4*i+2] = byte(u >> 16)
213 | bin[4*i+3] = byte(u >> 24)
214 | }
215 | if subok {
216 | ctx.BufferSubData(target, 0, bin)
217 | } else {
218 | ctx.BufferData(target, bin, usage)
219 | }
220 | }
221 | }
222 |
223 | func BufferUintData(target, usage gl.Enum) func(gl.Context, []uint32) {
224 | var bin []byte
225 | return func(ctx gl.Context, data []uint32) {
226 | subok := len(bin) > 0 && len(data)*4 <= len(bin)
227 | if !subok {
228 | bin = make([]byte, len(data)*4)
229 | }
230 | for i, u := range data {
231 | bin[4*i+0] = byte(u >> 0)
232 | bin[4*i+1] = byte(u >> 8)
233 | bin[4*i+2] = byte(u >> 16)
234 | bin[4*i+3] = byte(u >> 24)
235 | }
236 | if subok {
237 | ctx.BufferSubData(target, 0, bin)
238 | } else {
239 | ctx.BufferData(target, bin, usage)
240 | }
241 | }
242 | }
243 |
244 | type Texture struct{ gl.Texture }
245 |
246 | func (tex *Texture) Create(ctx gl.Context) {
247 | tex.Texture = ctx.CreateTexture()
248 | }
249 |
250 | func (tex Texture) Delete(ctx gl.Context) {
251 | ctx.DeleteTexture(tex.Texture)
252 | }
253 |
254 | func (tex Texture) Bind(ctx gl.Context, options ...func(gl.Context, Texture)) {
255 | ctx.ActiveTexture(gl.Enum(uint32(gl.TEXTURE0) + tex.Value - 1))
256 | ctx.BindTexture(gl.TEXTURE_2D, tex.Texture)
257 | for _, opt := range options {
258 | opt(ctx, tex)
259 | }
260 | }
261 |
262 | func (tex Texture) Unbind(ctx gl.Context) {
263 | ctx.BindTexture(gl.TEXTURE_2D, gl.Texture{Value: 0})
264 | }
265 |
266 | func (tex Texture) Update(ctx gl.Context, lvl int, width int, height int, data []byte) {
267 | ctx.TexImage2D(gl.TEXTURE_2D, lvl, int(gl.RGBA), width, height, gl.RGBA, gl.UNSIGNED_BYTE, data)
268 | if lvl > 0 {
269 | ctx.GenerateMipmap(gl.TEXTURE_2D)
270 | }
271 | }
272 |
273 | // TODO incorporate into Update, see FloatBuffer and UintBuffer
274 | func (tex Texture) Sub(ctx gl.Context, lvl int, width int, height int, data []byte) {
275 | ctx.TexSubImage2D(gl.TEXTURE_2D, lvl, 0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, data)
276 | if lvl > 0 {
277 | ctx.GenerateMipmap(gl.TEXTURE_2D)
278 | }
279 | }
280 |
281 | func TextureDef(lvl int, width, height int, format gl.Enum, data []byte) func(gl.Context, Texture) {
282 | return func(ctx gl.Context, tex Texture) {
283 | ctx.TexImage2D(gl.TEXTURE_2D, lvl, int(format), width, height, format, gl.UNSIGNED_BYTE, data)
284 | }
285 | }
286 |
287 | func TextureFilter(min, mag int) func(gl.Context, Texture) {
288 | return func(ctx gl.Context, tex Texture) {
289 | ctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, min)
290 | ctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, mag)
291 | }
292 | }
293 |
294 | func TextureWrap(s, t int) func(gl.Context, Texture) {
295 | return func(ctx gl.Context, tex Texture) {
296 | ctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, s)
297 | ctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, t)
298 | }
299 | }
300 |
301 | type Framebuffer struct{ gl.Framebuffer }
302 |
303 | func (fbo *Framebuffer) Create(ctx gl.Context) {
304 | fbo.Framebuffer = ctx.CreateFramebuffer()
305 | }
306 |
307 | func (fbo Framebuffer) Delete(ctx gl.Context) {
308 | ctx.DeleteFramebuffer(fbo.Framebuffer)
309 | }
310 |
311 | func (fbo Framebuffer) Bind(ctx gl.Context, options ...func(gl.Context, Framebuffer)) {
312 | ctx.BindFramebuffer(gl.FRAMEBUFFER, fbo.Framebuffer)
313 | for _, opt := range options {
314 | opt(ctx, fbo)
315 | }
316 | }
317 |
318 | func (fbo Framebuffer) Unbind(ctx gl.Context) {
319 | ctx.BindFramebuffer(gl.FRAMEBUFFER, gl.Framebuffer{Value: 0})
320 | }
321 |
322 | func FramebufferTex(tex Texture, lvl int) func(gl.Context, Framebuffer) {
323 | return func(ctx gl.Context, fbo Framebuffer) {
324 | ctx.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex.Texture, lvl)
325 | }
326 | }
327 |
328 | func FramebufferWithTex(tex Texture, lvl int, options ...func(gl.Context, Texture)) func(gl.Context, Framebuffer) {
329 | fbotex := FramebufferTex(tex, lvl)
330 | return func(ctx gl.Context, fbo Framebuffer) {
331 | tex.Bind(ctx, options...)
332 | fbotex(ctx, fbo)
333 | }
334 | }
335 |
--------------------------------------------------------------------------------
/color.go:
--------------------------------------------------------------------------------
1 | package material
2 |
3 | type Color uint32
4 |
5 | // RGBA returns the unit value of each component.
6 | func (c Color) RGBA() (r, g, b, a float32) {
7 | ur := uint8(c >> 24)
8 | ug := uint8(c >> 16)
9 | ub := uint8(c >> 8)
10 | ua := uint8(c)
11 | r, g, b, a = float32(ur)/255, float32(ug)/255, float32(ub)/255, float32(ua)/255
12 | return
13 | }
14 |
15 | func (c Color) RGBA64() (r, g, b, a float64) {
16 | ur := uint8(c >> 24)
17 | ug := uint8(c >> 16)
18 | ub := uint8(c >> 8)
19 | ua := uint8(c)
20 | r, g, b, a = float64(ur)/255, float64(ug)/255, float64(ub)/255, float64(ua)/255
21 | return
22 | }
23 |
24 | func (c Color) WithAlpha(a uint8) Color {
25 | return c ^ Color(255-a)
26 | }
27 |
28 | const (
29 | RedPrimary Color = Red500
30 | Red50 Color = 0xFFEBEEFF
31 | Red100 Color = 0xFFCDD2FF
32 | Red200 Color = 0xEF9A9AFF
33 | Red300 Color = 0xE57373FF
34 | Red400 Color = 0xEF5350FF
35 | Red500 Color = 0xF44336FF
36 | Red600 Color = 0xE53935FF
37 | Red700 Color = 0xD32F2FFF
38 | Red800 Color = 0xC62828FF
39 | Red900 Color = 0xB71C1CFF
40 | RedA100 Color = 0xFF8A80FF
41 | RedA200 Color = 0xFF5252FF
42 | RedA400 Color = 0xFF1744FF
43 | RedA700 Color = 0xD50000FF
44 |
45 | PinkPrimary Color = Pink500
46 | Pink50 Color = 0xFCE4ECFF
47 | Pink100 Color = 0xF8BBD0FF
48 | Pink200 Color = 0xF48FB1FF
49 | Pink300 Color = 0xF06292FF
50 | Pink400 Color = 0xEC407AFF
51 | Pink500 Color = 0xE91E63FF
52 | Pink600 Color = 0xD81B60FF
53 | Pink700 Color = 0xC2185BFF
54 | Pink800 Color = 0xAD1457FF
55 | Pink900 Color = 0x880E4FFF
56 | PinkA100 Color = 0xFF80ABFF
57 | PinkA200 Color = 0xFF4081FF
58 | PinkA400 Color = 0xF50057FF
59 | PinkA700 Color = 0xC51162FF
60 |
61 | PurplePrimary Color = Purple500
62 | Purple50 Color = 0xF3E5F5FF
63 | Purple100 Color = 0xE1BEE7FF
64 | Purple200 Color = 0xCE93D8FF
65 | Purple300 Color = 0xBA68C8FF
66 | Purple400 Color = 0xAB47BCFF
67 | Purple500 Color = 0x9C27B0FF
68 | Purple600 Color = 0x8E24AAFF
69 | Purple700 Color = 0x7B1FA2FF
70 | Purple800 Color = 0x6A1B9AFF
71 | Purple900 Color = 0x4A148CFF
72 | PurpleA100 Color = 0xEA80FCFF
73 | PurpleA200 Color = 0xE040FBFF
74 | PurpleA400 Color = 0xD500F9FF
75 | PurpleA700 Color = 0xAA00FFFF
76 |
77 | DeepPurplePrimary Color = DeepPurple500
78 | DeepPurple50 Color = 0xEDE7F6FF
79 | DeepPurple100 Color = 0xD1C4E9FF
80 | DeepPurple200 Color = 0xB39DDBFF
81 | DeepPurple300 Color = 0x9575CDFF
82 | DeepPurple400 Color = 0x7E57C2FF
83 | DeepPurple500 Color = 0x673AB7FF
84 | DeepPurple600 Color = 0x5E35B1FF
85 | DeepPurple700 Color = 0x512DA8FF
86 | DeepPurple800 Color = 0x4527A0FF
87 | DeepPurple900 Color = 0x311B92FF
88 | DeepPurpleA100 Color = 0xB388FFFF
89 | DeepPurpleA200 Color = 0x7C4DFFFF
90 | DeepPurpleA400 Color = 0x651FFFFF
91 | DeepPurpleA700 Color = 0x6200EAFF
92 |
93 | IndigoPrimary Color = Indigo500
94 | Indigo50 Color = 0xE8EAF6FF
95 | Indigo100 Color = 0xC5CAE9FF
96 | Indigo200 Color = 0x9FA8DAFF
97 | Indigo300 Color = 0x7986CBFF
98 | Indigo400 Color = 0x5C6BC0FF
99 | Indigo500 Color = 0x3F51B5FF
100 | Indigo600 Color = 0x3949ABFF
101 | Indigo700 Color = 0x303F9FFF
102 | Indigo800 Color = 0x283593FF
103 | Indigo900 Color = 0x1A237EFF
104 | IndigoA100 Color = 0x8C9EFFFF
105 | IndigoA200 Color = 0x536DFEFF
106 | IndigoA400 Color = 0x3D5AFEFF
107 | IndigoA700 Color = 0x304FFEFF
108 |
109 | BluePrimary Color = Blue500
110 | Blue50 Color = 0xE3F2FDFF
111 | Blue100 Color = 0xBBDEFBFF
112 | Blue200 Color = 0x90CAF9FF
113 | Blue300 Color = 0x64B5F6FF
114 | Blue400 Color = 0x42A5F5FF
115 | Blue500 Color = 0x2196F3FF
116 | Blue600 Color = 0x1E88E5FF
117 | Blue700 Color = 0x1976D2FF
118 | Blue800 Color = 0x1565C0FF
119 | Blue900 Color = 0x0D47A1FF
120 | BlueA100 Color = 0x82B1FFFF
121 | BlueA200 Color = 0x448AFFFF
122 | BlueA400 Color = 0x2979FFFF
123 | BlueA700 Color = 0x2962FFFF
124 |
125 | LightBluePrimary Color = LightBlue500
126 | LightBlue50 Color = 0xE1F5FEFF
127 | LightBlue100 Color = 0xB3E5FCFF
128 | LightBlue200 Color = 0x81D4FAFF
129 | LightBlue300 Color = 0x4FC3F7FF
130 | LightBlue400 Color = 0x29B6F6FF
131 | LightBlue500 Color = 0x03A9F4FF
132 | LightBlue600 Color = 0x039BE5FF
133 | LightBlue700 Color = 0x0288D1FF
134 | LightBlue800 Color = 0x0277BDFF
135 | LightBlue900 Color = 0x01579BFF
136 | LightBlueA100 Color = 0x80D8FFFF
137 | LightBlueA200 Color = 0x40C4FFFF
138 | LightBlueA400 Color = 0x00B0FFFF
139 | LightBlueA700 Color = 0x0091EAFF
140 |
141 | CyanPrimary Color = Cyan500
142 | Cyan50 Color = 0xE0F7FAFF
143 | Cyan100 Color = 0xB2EBF2FF
144 | Cyan200 Color = 0x80DEEAFF
145 | Cyan300 Color = 0x4DD0E1FF
146 | Cyan400 Color = 0x26C6DAFF
147 | Cyan500 Color = 0x00BCD4FF
148 | Cyan600 Color = 0x00ACC1FF
149 | Cyan700 Color = 0x0097A7FF
150 | Cyan800 Color = 0x00838FFF
151 | Cyan900 Color = 0x006064FF
152 | CyanA100 Color = 0x84FFFFFF
153 | CyanA200 Color = 0x18FFFFFF
154 | CyanA400 Color = 0x00E5FFFF
155 | CyanA700 Color = 0x00B8D4FF
156 |
157 | TealPrimary Color = Teal500
158 | Teal50 Color = 0xE0F2F1FF
159 | Teal100 Color = 0xB2DFDBFF
160 | Teal200 Color = 0x80CBC4FF
161 | Teal300 Color = 0x4DB6ACFF
162 | Teal400 Color = 0x26A69AFF
163 | Teal500 Color = 0x009688FF
164 | Teal600 Color = 0x00897BFF
165 | Teal700 Color = 0x00796BFF
166 | Teal800 Color = 0x00695CFF
167 | Teal900 Color = 0x004D40FF
168 | TealA100 Color = 0xA7FFEBFF
169 | TealA200 Color = 0x64FFDAFF
170 | TealA400 Color = 0x1DE9B6FF
171 | TealA700 Color = 0x00BFA5FF
172 |
173 | GreenPrimary Color = Green500
174 | Green50 Color = 0xE8F5E9FF
175 | Green100 Color = 0xC8E6C9FF
176 | Green200 Color = 0xA5D6A7FF
177 | Green300 Color = 0x81C784FF
178 | Green400 Color = 0x66BB6AFF
179 | Green500 Color = 0x4CAF50FF
180 | Green600 Color = 0x43A047FF
181 | Green700 Color = 0x388E3CFF
182 | Green800 Color = 0x2E7D32FF
183 | Green900 Color = 0x1B5E20FF
184 | GreenA100 Color = 0xB9F6CAFF
185 | GreenA200 Color = 0x69F0AEFF
186 | GreenA400 Color = 0x00E676FF
187 | GreenA700 Color = 0x00C853FF
188 |
189 | LightGreenPrimary Color = LightGreen500
190 | LightGreen50 Color = 0xF1F8E9FF
191 | LightGreen100 Color = 0xDCEDC8FF
192 | LightGreen200 Color = 0xC5E1A5FF
193 | LightGreen300 Color = 0xAED581FF
194 | LightGreen400 Color = 0x9CCC65FF
195 | LightGreen500 Color = 0x8BC34AFF
196 | LightGreen600 Color = 0x7CB342FF
197 | LightGreen700 Color = 0x689F38FF
198 | LightGreen800 Color = 0x558B2FFF
199 | LightGreen900 Color = 0x33691EFF
200 | LightGreenA100 Color = 0xCCFF90FF
201 | LightGreenA200 Color = 0xB2FF59FF
202 | LightGreenA400 Color = 0x76FF03FF
203 | LightGreenA700 Color = 0x64DD17FF
204 |
205 | LimePrimary Color = Lime500
206 | Lime50 Color = 0xF9FBE7FF
207 | Lime100 Color = 0xF0F4C3FF
208 | Lime200 Color = 0xE6EE9CFF
209 | Lime300 Color = 0xDCE775FF
210 | Lime400 Color = 0xD4E157FF
211 | Lime500 Color = 0xCDDC39FF
212 | Lime600 Color = 0xC0CA33FF
213 | Lime700 Color = 0xAFB42BFF
214 | Lime800 Color = 0x9E9D24FF
215 | Lime900 Color = 0x827717FF
216 | LimeA100 Color = 0xF4FF81FF
217 | LimeA200 Color = 0xEEFF41FF
218 | LimeA400 Color = 0xC6FF00FF
219 | LimeA700 Color = 0xAEEA00FF
220 |
221 | YellowPrimary Color = Yellow500
222 | Yellow50 Color = 0xFFFDE7FF
223 | Yellow100 Color = 0xFFF9C4FF
224 | Yellow200 Color = 0xFFF59DFF
225 | Yellow300 Color = 0xFFF176FF
226 | Yellow400 Color = 0xFFEE58FF
227 | Yellow500 Color = 0xFFEB3BFF
228 | Yellow600 Color = 0xFDD835FF
229 | Yellow700 Color = 0xFBC02DFF
230 | Yellow800 Color = 0xF9A825FF
231 | Yellow900 Color = 0xF57F17FF
232 | YellowA100 Color = 0xFFFF8DFF
233 | YellowA200 Color = 0xFFFF00FF
234 | YellowA400 Color = 0xFFEA00FF
235 | YellowA700 Color = 0xFFD600FF
236 |
237 | AmberPrimary Color = Amber500
238 | Amber50 Color = 0xFFF8E1FF
239 | Amber100 Color = 0xFFECB3FF
240 | Amber200 Color = 0xFFE082FF
241 | Amber300 Color = 0xFFD54FFF
242 | Amber400 Color = 0xFFCA28FF
243 | Amber500 Color = 0xFFC107FF
244 | Amber600 Color = 0xFFB300FF
245 | Amber700 Color = 0xFFA000FF
246 | Amber800 Color = 0xFF8F00FF
247 | Amber900 Color = 0xFF6F00FF
248 | AmberA100 Color = 0xFFE57FFF
249 | AmberA200 Color = 0xFFD740FF
250 | AmberA400 Color = 0xFFC400FF
251 | AmberA700 Color = 0xFFAB00FF
252 |
253 | OrangePrimary Color = Orange500
254 | Orange50 Color = 0xFFF3E0FF
255 | Orange100 Color = 0xFFE0B2FF
256 | Orange200 Color = 0xFFCC80FF
257 | Orange300 Color = 0xFFB74DFF
258 | Orange400 Color = 0xFFA726FF
259 | Orange500 Color = 0xFF9800FF
260 | Orange600 Color = 0xFB8C00FF
261 | Orange700 Color = 0xF57C00FF
262 | Orange800 Color = 0xEF6C00FF
263 | Orange900 Color = 0xE65100FF
264 | OrangeA100 Color = 0xFFD180FF
265 | OrangeA200 Color = 0xFFAB40FF
266 | OrangeA400 Color = 0xFF9100FF
267 | OrangeA700 Color = 0xFF6D00FF
268 |
269 | DeepOrangePrimary Color = DeepOrange500
270 | DeepOrange50 Color = 0xFBE9E7FF
271 | DeepOrange100 Color = 0xFFCCBCFF
272 | DeepOrange200 Color = 0xFFAB91FF
273 | DeepOrange300 Color = 0xFF8A65FF
274 | DeepOrange400 Color = 0xFF7043FF
275 | DeepOrange500 Color = 0xFF5722FF
276 | DeepOrange600 Color = 0xF4511EFF
277 | DeepOrange700 Color = 0xE64A19FF
278 | DeepOrange800 Color = 0xD84315FF
279 | DeepOrange900 Color = 0xBF360CFF
280 | DeepOrangeA100 Color = 0xFF9E80FF
281 | DeepOrangeA200 Color = 0xFF6E40FF
282 | DeepOrangeA400 Color = 0xFF3D00FF
283 | DeepOrangeA700 Color = 0xDD2C00FF
284 |
285 | BrownPrimary Color = Brown500
286 | Brown50 Color = 0xEFEBE9FF
287 | Brown100 Color = 0xD7CCC8FF
288 | Brown200 Color = 0xBCAAA4FF
289 | Brown300 Color = 0xA1887FFF
290 | Brown400 Color = 0x8D6E63FF
291 | Brown500 Color = 0x795548FF
292 | Brown600 Color = 0x6D4C41FF
293 | Brown700 Color = 0x5D4037FF
294 | Brown800 Color = 0x4E342EFF
295 | Brown900 Color = 0x3E2723FF
296 |
297 | GreyPrimary Color = Grey500
298 | Grey50 Color = 0xFAFAFAFF
299 | Grey100 Color = 0xF5F5F5FF
300 | Grey200 Color = 0xEEEEEEFF
301 | Grey300 Color = 0xE0E0E0FF
302 | Grey400 Color = 0xBDBDBDFF
303 | Grey500 Color = 0x9E9E9EFF
304 | Grey600 Color = 0x757575FF
305 | Grey700 Color = 0x616161FF
306 | Grey800 Color = 0x424242FF
307 | Grey900 Color = 0x212121FF
308 |
309 | BlueGreyPrimary Color = BlueGrey500
310 | BlueGrey50 Color = 0xECEFF1FF
311 | BlueGrey100 Color = 0xCFD8DCFF
312 | BlueGrey200 Color = 0xB0BEC5FF
313 | BlueGrey300 Color = 0x90A4AEFF
314 | BlueGrey400 Color = 0x78909CFF
315 | BlueGrey500 Color = 0x607D8BFF
316 | BlueGrey600 Color = 0x546E7AFF
317 | BlueGrey700 Color = 0x455A64FF
318 | BlueGrey800 Color = 0x37474FFF
319 | BlueGrey900 Color = 0x263238FF
320 |
321 | Black Color = 0x000000FF
322 | White Color = 0xFFFFFFFF
323 | )
324 |
325 | type Palette struct {
326 | Primary, Dark, Light Color
327 | Accent Color
328 | }
329 |
--------------------------------------------------------------------------------
/text/gen.go:
--------------------------------------------------------------------------------
1 | // +build ignore
2 |
3 | package main
4 |
5 | import (
6 | "bytes"
7 | "flag"
8 | "fmt"
9 | "image"
10 | "image/color"
11 | "image/draw"
12 | "image/png"
13 | "io/ioutil"
14 | "log"
15 | "math"
16 | "os"
17 | "path/filepath"
18 | "sort"
19 | "sync"
20 |
21 | "github.com/golang/freetype/truetype"
22 | "github.com/nfnt/resize"
23 | "golang.org/x/image/font"
24 | "golang.org/x/image/math/fixed"
25 | )
26 |
27 | var (
28 | flagOutdir = flag.String("outdir", ".", "directory to write generated files")
29 | flagPkgname = flag.String("pkgname", "text", "package name of generated go source files")
30 | flagFontfile = flag.String("fontfile", "", "filename of the ttf font")
31 | flagTSize = flag.Int("tsize", 2048, "width and height of texture; 0 means pick smallest power of 2")
32 | flagFSize = flag.Float64("fsize", 72, "font size in points")
33 | flagPad = flag.Int("pad", 4, "amounted of padding for calculating sdf")
34 | flagScale = flag.Int("scale", 1, "scale inputs for calculating sdf, linear resizing final ouput to inputs")
35 | flagBorder = flag.Int("border", 1, "space around glyph")
36 | flagAscii = flag.Bool("ascii", false, "only process ascii glyphs")
37 | )
38 |
39 | var ascii = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789`~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/? "
40 |
41 | const EdgeAlpha = 0x7f
42 |
43 | type SDF struct {
44 | src *image.NRGBA // where glyphs are first written
45 | dst *image.NRGBA // where sdf calculation is written
46 | out *image.NRGBA // final output, scaled if needed
47 |
48 | tsize int
49 | fsize float64
50 | pad int
51 | border int
52 |
53 | padr int
54 | }
55 |
56 | func NewSDF(textureSize int, fontSize float64, pad int, scale int, border int) *SDF {
57 | sdf := &SDF{
58 | tsize: textureSize * scale,
59 | fsize: fontSize * float64(scale),
60 | pad: pad * scale,
61 | padr: pad,
62 | border: border * scale,
63 | }
64 |
65 | sdf.src = image.NewNRGBA(image.Rect(0, 0, sdf.tsize, sdf.tsize))
66 | sdf.dst = image.NewNRGBA(image.Rect(0, 0, sdf.tsize, sdf.tsize))
67 | if scale > 1 {
68 | sdf.out = image.NewNRGBA(image.Rect(0, 0, textureSize, textureSize))
69 | } else {
70 | sdf.out = image.NewNRGBA(image.Rect(0, 0, sdf.tsize, sdf.tsize))
71 | }
72 |
73 | return sdf
74 | }
75 |
76 | func (sdf *SDF) writeSrc() {
77 | out, err := os.Create(filepath.Join(*flagOutdir, "src.png"))
78 | if err != nil {
79 | log.Fatal(err)
80 | }
81 | defer out.Close()
82 |
83 | if err := png.Encode(out, sdf.src); err != nil {
84 | log.Fatal(err)
85 | }
86 | }
87 |
88 | func (sdf *SDF) writeDst() {
89 | out, err := os.Create(filepath.Join(*flagOutdir, "dst.png"))
90 | if err != nil {
91 | log.Fatal(err)
92 | }
93 | defer out.Close()
94 |
95 | if err := png.Encode(out, sdf.dst); err != nil {
96 | log.Fatal(err)
97 | }
98 | }
99 |
100 | func (sdf *SDF) writeOut() {
101 | out, err := os.Create(filepath.Join(*flagOutdir, "out.png"))
102 | if err != nil {
103 | log.Fatal(err)
104 | }
105 | defer out.Close()
106 |
107 | if sdf.dst.Bounds().Eq(sdf.out.Bounds()) {
108 | draw.Draw(sdf.out, sdf.out.Bounds(), sdf.dst, image.ZP, draw.Src)
109 | } else {
110 | b := sdf.out.Bounds()
111 | // TODO get back to bilinear. Originally did bilinear with alpha only image which
112 | // works well, but this bilinear filter with the new color images doesn't preserve
113 | // colors well. Could be NRGBA use, could just be the lib, but gimp does what's desired
114 | // with linear filter on resize.
115 | rs := resize.Resize(uint(b.Dx()), uint(b.Dy()), sdf.dst, resize.Bilinear)
116 | draw.Draw(sdf.out, sdf.out.Bounds(), rs, image.ZP, draw.Over)
117 | }
118 |
119 | if err := png.Encode(out, sdf.out); err != nil {
120 | log.Fatal(err)
121 | }
122 | }
123 |
124 | func (sdf *SDF) calc(m image.Image) {
125 | max := dist(0, 0, sdf.pad, sdf.pad) - 1
126 | b := m.Bounds()
127 | for y := b.Min.Y; y < b.Max.Y; y++ {
128 | for x := b.Min.X; x < b.Max.X; x++ {
129 | ma := m.At(x, y).(color.NRGBA).A
130 | c := nearest(x, y, m.(*image.NRGBA).SubImage(image.Rect(x-sdf.pad, y-sdf.pad, x+sdf.pad, y+sdf.pad)))
131 | if c == 0xFF {
132 | // check if pixel is inside as a center of opposing edges
133 | if ma != 0 {
134 | sdf.dst.Set(x, y, color.RGBA{A: 0xFF})
135 | // sdf.dst.Set(x, y, color.White)
136 | }
137 | continue
138 | }
139 |
140 | // return from nearest is always >= 1
141 | // decrement so that c/max returns a unit value inclusive of zero
142 | c--
143 |
144 | n := 0xFF * (1 - (float64(c) / float64(max)))
145 | if ma != 0 { // inside edge
146 | sdf.dst.Set(x, y, color.RGBA{A: 0xFF - uint8(n/2)})
147 | } else { // outside edge
148 | step := float64(0xFF) / float64(max)
149 | if n = n - step; n < 0 {
150 | n = 0
151 | }
152 | sdf.dst.Set(x, y, color.RGBA{A: uint8(n / 2)})
153 | }
154 | }
155 | }
156 | }
157 |
158 | // nearest returns the distance to the closest pixel of
159 | // opposite color from (mx, my) in a subspace.
160 | func nearest(mx, my int, m image.Image) float64 {
161 | var min float64 = 0xFF
162 | ma := m.At(mx, my).(color.NRGBA).A
163 | b := m.Bounds()
164 | for y := b.Min.Y; y < b.Max.Y; y++ {
165 | for x := b.Min.X; x < b.Max.X; x++ {
166 | a := m.At(x, y).(color.NRGBA).A
167 | // check against zero guards against bad input
168 | // to consistently give the desired 1px border.
169 | if (ma == 0 && a != 0) || (ma != 0 && a == 0) { // implicitly prevents check against itself
170 | dt := dist(mx, my, x, y)
171 | if min > dt {
172 | min = dt
173 | }
174 | if min == 1 { // minimum-bound reached, return early
175 | return min
176 | }
177 | }
178 | }
179 | }
180 | return min
181 | }
182 |
183 | // dist returns distance between two points.
184 | func dist(x0, y0, x1, y1 int) float64 {
185 | x, y := x1-x0, y1-y0
186 | return math.Sqrt(float64(x*x + y*y))
187 | }
188 |
189 | type glyph struct {
190 | r rune
191 | b fixed.Rectangle26_6
192 | a fixed.Int26_6
193 | tc [4]float32
194 | }
195 |
196 | func (g *glyph) width() int { return (g.b.Max.X - g.b.Min.X).Ceil() }
197 | func (g *glyph) height() int { return (g.b.Max.Y - g.b.Min.Y).Ceil() }
198 |
199 | // area total all glyphs occupy.
200 | func area(a []*glyph, pad int) int {
201 | var n int
202 | for _, g := range a {
203 | n += (g.width() + pad*2) * (g.height() + pad*2)
204 | }
205 | return n
206 | }
207 |
208 | // enumerate returns all glyphs with a valid index in font.
209 | func enumerate(f *truetype.Font, fc font.Face) []*glyph {
210 | var gs []*glyph
211 | for r := rune(1); r < (1<<16)-1; r++ {
212 | if r == '\uFEFF' {
213 | continue // ignore BOM
214 | }
215 | if f.Index(r) != 0 {
216 | b, a, _ := fc.GlyphBounds(r)
217 | gs = append(gs, &glyph{r: r, b: b, a: a})
218 | }
219 | }
220 | return gs
221 | }
222 |
223 | func enumerateString(s string, fc font.Face) []*glyph {
224 | var gs []*glyph
225 | for _, r := range s {
226 | if b, a, ok := fc.GlyphBounds(r); ok {
227 | gs = append(gs, &glyph{r: r, b: b, a: a})
228 | }
229 | }
230 | return gs
231 | }
232 |
233 | // byHeight sorts glyphs tallest to shortest.
234 | type byHeight []*glyph
235 |
236 | func (a byHeight) Len() int { return len(a) }
237 | func (a byHeight) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
238 | func (a byHeight) Less(i, j int) bool { return a[j].height() < a[i].height() }
239 |
240 | func main() {
241 | flag.Parse()
242 |
243 | if *flagFontfile == "" || *flagOutdir == "" || *flagPkgname == "" {
244 | flag.Usage()
245 | return
246 | }
247 |
248 | if *flagScale < 1 {
249 | log.Println("scale must be >= 1")
250 | return
251 | }
252 |
253 | bin, err := ioutil.ReadFile(*flagFontfile)
254 | if err != nil {
255 | log.Println(err)
256 | return
257 | }
258 | f, err := truetype.Parse(bin)
259 | if err != nil {
260 | log.Println(err)
261 | return
262 | }
263 |
264 | sdf := NewSDF(*flagTSize, *flagFSize, *flagPad, *flagScale, *flagBorder)
265 | d := &font.Drawer{
266 | Dst: sdf.src,
267 | Src: image.Black,
268 | Face: truetype.NewFace(f, &truetype.Options{
269 | Size: sdf.fsize,
270 | Hinting: font.HintingFull,
271 | }),
272 | }
273 |
274 | var glyphs []*glyph
275 | if *flagAscii {
276 | glyphs = enumerateString(ascii, d.Face)
277 | } else {
278 | glyphs = enumerate(f, d.Face)
279 | }
280 | if len(glyphs) == 0 {
281 | log.Fatalf("sdf: failed to enumerate glyphs from %s\n", *flagFontfile)
282 | }
283 | if a := area(glyphs, sdf.pad); a > sdf.tsize*sdf.tsize {
284 | asq := math.Sqrt(float64(a))
285 | log.Fatalf("sdf: glyphs area %[1]v ~= %.2[2]fx%.2[2]f greater than texture area %[3]vx%[3]v\n", a, asq, sdf.tsize)
286 | }
287 | sort.Sort(byHeight(glyphs))
288 |
289 | x, y, dy := 0, 0, glyphs[0].height()+sdf.pad*2+sdf.border*2
290 | var wg sync.WaitGroup
291 | for _, g := range glyphs {
292 | adx, ady := g.width()+sdf.pad*2+sdf.border*2, g.height()+sdf.pad*2+sdf.border*2
293 | if x+adx > sdf.tsize {
294 | x = 0
295 | y += dy
296 | dy = ady
297 | }
298 |
299 | g.tc = [4]float32{
300 | // float32(x+sdf.pad+sdf.border) / float32(sdf.tsize),
301 | // float32(y+sdf.pad+sdf.border) / float32(sdf.tsize),
302 | float32(x) / float32(sdf.tsize),
303 | float32(y) / float32(sdf.tsize),
304 | float32(g.width()+sdf.pad*2+sdf.border*2) / float32(sdf.tsize),
305 | float32(g.height()+sdf.pad*2+sdf.border*2) / float32(sdf.tsize),
306 | }
307 |
308 | d.Dot = fixed.P(x+sdf.pad+sdf.border-int(g.b.Min.X>>6), y+sdf.pad+sdf.border-g.b.Min.Y.Ceil())
309 | d.DrawString(string(g.r))
310 |
311 | wg.Add(1)
312 | go func(m image.Image) {
313 | sdf.calc(m)
314 | wg.Done()
315 | }(sdf.src.SubImage(image.Rect(x, y, x+adx, y+ady))) // TODO workout border issues; then stop passing in dead space to sdf calc
316 |
317 | x += adx
318 | }
319 | wg.Wait()
320 |
321 | sdf.writeSrc()
322 | sdf.writeDst()
323 | sdf.writeOut()
324 |
325 | // generate source file to accompany out.png
326 | buf := new(bytes.Buffer)
327 | buf.WriteString("// generated by gen.go; DO NOT EDIT\n")
328 | fmt.Fprintf(buf, "package %s\n\n", *flagPkgname)
329 |
330 | ascent := float32(d.Face.Metrics().Ascent.Ceil())
331 | descent := float32(d.Face.Metrics().Descent.Floor())
332 | scale := float32(sdf.fsize) / (ascent + descent)
333 | fmt.Fprintf(buf, "const AscentUnit = %v\n", (ascent*scale)/float32(sdf.fsize))
334 | fmt.Fprintf(buf, "const DescentUnit = %v\n", (descent*scale)/float32(sdf.fsize))
335 | fmt.Fprintf(buf, "const TextureSize = %v\n", *flagTSize)
336 | fmt.Fprintf(buf, "const FontSize = %v\n", *flagFSize)
337 | fmt.Fprintf(buf, "const Pad = %v\n\n", *flagPad)
338 |
339 | buf.WriteString("var Texcoords = map[rune][4]float32{\n")
340 | for _, g := range glyphs {
341 | s := string(g.r)
342 | if s == "'" {
343 | s = `\'`
344 | } else if s == "\\" {
345 | s = `\\`
346 | }
347 | fmt.Fprintf(buf, "\t'%s': {%v, %v, %v, %v},\n", s, g.tc[0], g.tc[1], g.tc[2], g.tc[3])
348 | }
349 | buf.WriteString("}\n\n")
350 |
351 | buf.WriteString("var Bounds = map[rune][5]float32{\n")
352 | for _, g := range glyphs {
353 | s := string(g.r)
354 | if s == "'" {
355 | s = `\'`
356 | } else if s == "\\" {
357 | s = `\\`
358 | }
359 | nx := float32(g.b.Min.X>>6) / float32(sdf.fsize)
360 | ny := float32(g.b.Max.Y>>6) / float32(sdf.fsize)
361 | rect := g.b.Max.Sub(g.b.Min)
362 | w, h := float32(rect.X>>6), float32(rect.Y>>6)
363 | nw := float32(w) / float32(sdf.fsize)
364 | nh := float32(h) / float32(sdf.fsize)
365 | na := float32(g.a>>6) / float32(sdf.fsize)
366 | fmt.Fprintf(buf, "\t'%s': {%v, %v, %v, %v, %v},\n", s, nx, ny, nw, nh, na)
367 | }
368 | buf.WriteString("}\n\n")
369 |
370 | if err := ioutil.WriteFile(filepath.Join(*flagOutdir, "texcoords.go"), buf.Bytes(), 0644); err != nil {
371 | log.Fatal(err)
372 | }
373 | }
374 |
--------------------------------------------------------------------------------
/environment.go:
--------------------------------------------------------------------------------
1 | package material
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "image"
7 | "image/draw"
8 | "log"
9 | "sort"
10 | "time"
11 | "unicode"
12 |
13 | _ "image/gif"
14 | _ "image/jpeg"
15 | _ "image/png"
16 |
17 | _ "golang.org/x/image/bmp"
18 | _ "golang.org/x/image/tiff"
19 | _ "golang.org/x/image/webp"
20 |
21 | "dasa.cc/material/assets"
22 | "dasa.cc/material/glutil"
23 | "dasa.cc/material/icon"
24 | "dasa.cc/material/text"
25 | "dasa.cc/simplex"
26 |
27 | "golang.org/x/mobile/event/size"
28 | "golang.org/x/mobile/event/touch"
29 | "golang.org/x/mobile/exp/f32"
30 | "golang.org/x/mobile/gl"
31 | )
32 |
33 | type Dp float32
34 |
35 | func (dp Dp) Px() float32 {
36 | if windowSize.PixelsPerPt == 1 {
37 | return float32(dp)
38 | }
39 | density := windowSize.PixelsPerPt * 72
40 | return float32(dp) * density / 160
41 | }
42 |
43 | type Sheet interface {
44 | // Draw(ctx gl.Context, view, proj f32.Mat4)
45 | Bind(*simplex.Program)
46 | UpdateWorld(*simplex.Program)
47 | Contains(x, y float32) bool
48 | M() *Material
49 | Constraints(*Environment) []simplex.Constraint
50 | Hidden() bool
51 | }
52 |
53 | type byZ []Sheet
54 |
55 | func (a byZ) Len() int { return len(a) }
56 | func (a byZ) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
57 | func (a byZ) Less(i, j int) bool { return a[i].M().world[2][3] < a[j].M().world[2][3] }
58 |
59 | type Environment struct {
60 | View f32.Mat4
61 |
62 | proj f32.Mat4
63 | plt Palette
64 | sheets []Sheet
65 |
66 | Box Box
67 | Grid *Grid
68 |
69 | lprg *simplex.Program
70 |
71 | icons glutil.Texture
72 | glyphs glutil.Texture
73 |
74 | image glutil.Texture
75 |
76 | prg glutil.Program
77 |
78 | uniforms struct {
79 | view, proj, shadowColor gl.Uniform
80 | glyphs, icons, image gl.Uniform
81 | glyphconf gl.Uniform
82 | }
83 |
84 | attribs struct {
85 | vertex, color, dist, texcoord gl.Attrib
86 | touch gl.Attrib
87 | }
88 |
89 | buffers struct {
90 | verts, colors, dists, texcoords glutil.FloatBuffer
91 | indices glutil.UintBuffer
92 | touches glutil.FloatBuffer
93 | }
94 |
95 | verts, colors, dists, texcoords []float32
96 | indices []uint32
97 | touches []float32
98 |
99 | watchEvent chan string
100 | watchQuit chan bool
101 | }
102 |
103 | func (env *Environment) Proj() f32.Mat4 { return env.proj }
104 |
105 | func (env *Environment) Size() size.Event {
106 | return windowSize
107 | }
108 |
109 | func (env *Environment) WatchShaders() {
110 | env.watchEvent, env.watchQuit = watchShaders()
111 | }
112 |
113 | func (env *Environment) LoadIcons(ctx gl.Context) {
114 | src, _, err := image.Decode(glutil.MustOpen("material/material-icons-black-mdpi.png"))
115 | if err != nil {
116 | log.Fatal(err)
117 | }
118 |
119 | r := image.Rect(0, 0, 2048, 2048)
120 | dst := image.NewNRGBA(r)
121 | // pt := image.Point{0, -(2048 - src.Bounds().Size().Y)}
122 | draw.Draw(dst, r, src, image.ZP, draw.Src)
123 |
124 | // f, _ := os.Create("debug-icons.png")
125 | // png.Encode(f, dst)
126 |
127 | env.icons.Create(ctx)
128 | env.icons.Bind(ctx, nearestFilter, DefaultWrap)
129 | env.icons.Update(ctx, 0, 2048, 2048, dst.Pix)
130 | }
131 |
132 | var (
133 | imageSize image.Point
134 | imageTextureSize = 2048
135 | )
136 |
137 | func (env *Environment) LoadImage(ctx gl.Context, name string) image.Point {
138 | src, _, err := image.Decode(glutil.MustOpen(name))
139 | if err != nil {
140 | log.Fatal(err)
141 | }
142 |
143 | imageSize = src.Bounds().Size()
144 | r := image.Rect(0, 0, imageTextureSize, imageTextureSize)
145 | dst := image.NewNRGBA(r)
146 | draw.Draw(dst, r, src, image.ZP, draw.Src)
147 |
148 | env.image.Create(ctx)
149 | env.image.Bind(ctx, nearestFilter, DefaultWrap)
150 | env.image.Update(ctx, 0, 2048, 2048, dst.Pix)
151 |
152 | return imageSize
153 | }
154 |
155 | func (env *Environment) LoadGlyphs(ctx gl.Context) {
156 | src, _, err := image.Decode(bytes.NewReader(text.Texture)) // image.Decode(glutil.MustOpen("material/glyphs.png"))
157 | if err != nil {
158 | log.Fatal(err)
159 | }
160 | env.glyphs.Create(ctx)
161 | env.glyphs.Bind(ctx, nearestFilter, DefaultWrap)
162 | switch src.(type) {
163 | case *image.RGBA:
164 | env.glyphs.Update(ctx, 0, text.TextureSize, text.TextureSize, src.(*image.RGBA).Pix)
165 | case *image.NRGBA:
166 | env.glyphs.Update(ctx, 0, text.TextureSize, text.TextureSize, src.(*image.NRGBA).Pix)
167 | default:
168 | panic(fmt.Errorf("Unhandled image type %T", src))
169 | }
170 | }
171 |
172 | func (env *Environment) Load(ctx gl.Context) {
173 | env.prg.CreateAndLink(ctx,
174 | glutil.ShaderCompile(gl.VERTEX_SHADER, "env-vert.glsl", assets.VertexShader),
175 | glutil.ShaderCompile(gl.FRAGMENT_SHADER, "env-frag.glsl", assets.FragmentShader))
176 |
177 | env.uniforms.view = env.prg.Uniform(ctx, "view")
178 | env.uniforms.proj = env.prg.Uniform(ctx, "proj")
179 | env.uniforms.shadowColor = env.prg.Uniform(ctx, "shadowColor")
180 | env.uniforms.glyphs = env.prg.Uniform(ctx, "texglyph")
181 | env.uniforms.icons = env.prg.Uniform(ctx, "texicon")
182 | env.uniforms.glyphconf = env.prg.Uniform(ctx, "glyphconf")
183 | env.uniforms.image = env.prg.Uniform(ctx, "image")
184 |
185 | env.attribs.vertex = env.prg.Attrib(ctx, "vertex")
186 | env.attribs.color = env.prg.Attrib(ctx, "color")
187 | env.attribs.dist = env.prg.Attrib(ctx, "dist")
188 | env.attribs.texcoord = env.prg.Attrib(ctx, "texcoord")
189 | env.attribs.touch = env.prg.Attrib(ctx, "touch")
190 |
191 | env.buffers.indices = glutil.NewUintBuffer(ctx, []uint32{}, gl.STREAM_DRAW)
192 | env.buffers.verts = glutil.NewFloatBuffer(ctx, []float32{}, gl.STREAM_DRAW)
193 | env.buffers.colors = glutil.NewFloatBuffer(ctx, []float32{}, gl.STREAM_DRAW)
194 | env.buffers.dists = glutil.NewFloatBuffer(ctx, []float32{}, gl.STREAM_DRAW)
195 | env.buffers.texcoords = glutil.NewFloatBuffer(ctx, []float32{}, gl.STREAM_DRAW)
196 | env.buffers.touches = glutil.NewFloatBuffer(ctx, []float32{}, gl.STREAM_DRAW)
197 | }
198 |
199 | func (env *Environment) Unload(ctx gl.Context) {
200 | env.prg.Delete(ctx)
201 | env.buffers.indices.Delete(ctx)
202 | env.buffers.verts.Delete(ctx)
203 | env.buffers.colors.Delete(ctx)
204 | env.buffers.dists.Delete(ctx)
205 | env.buffers.texcoords.Delete(ctx)
206 | env.buffers.touches.Delete(ctx)
207 | if env.glyphs.Value != 0 {
208 | env.glyphs.Delete(ctx)
209 | }
210 | if env.icons.Value != 0 {
211 | env.icons.Delete(ctx)
212 | }
213 | env.sheets = env.sheets[:0]
214 | }
215 |
216 | func (env *Environment) SetPerspective(sz size.Event) {
217 | windowSize = sz
218 | env.Grid = NewGrid()
219 | env.View.Identity() // TODO not here, only on creation
220 | env.proj.Identity()
221 | glutil.Perspective(&env.proj, 0, float32(sz.WidthPx), 0, float32(sz.HeightPx))
222 | }
223 |
224 | func (env *Environment) SetOrtho(sz size.Event) {
225 | windowSize = sz
226 | env.Grid = NewGrid()
227 | env.View.Identity() // TODO not here, only on creation
228 | env.proj.Identity()
229 | glutil.Ortho(&env.proj, 0, float32(sz.WidthPx), 0, float32(sz.HeightPx), 1, 10000)
230 | env.View.Translate(&env.View, 0, 0, -5000)
231 | }
232 |
233 | func (env *Environment) Palette() Palette { return env.plt }
234 |
235 | func (env *Environment) SetPalette(plt Palette) {
236 | env.plt = plt
237 | for _, sheet := range env.sheets {
238 | switch sheet := sheet.(type) {
239 | case *Button:
240 | sheet.SetColor(env.plt.Primary)
241 | case *Toolbar:
242 | sheet.SetColor(env.plt.Light)
243 | }
244 | }
245 | }
246 |
247 | func (env *Environment) StartLayout() {
248 | env.lprg = new(simplex.Program)
249 | env.Box = NewBox(env.lprg)
250 | for _, sheet := range env.sheets {
251 | sheet.Bind(env.lprg)
252 | }
253 | env.AddConstraints(
254 | env.Box.Width(float32(windowSize.WidthPx)),
255 | env.Box.Height(float32(windowSize.HeightPx)),
256 | env.Box.Z(0),
257 | env.Box.Start(0), env.Box.Top(float32(windowSize.HeightPx)),
258 | )
259 | for _, sheet := range env.sheets {
260 | env.AddConstraints(sheet.Constraints(env)...)
261 | }
262 | }
263 |
264 | func (env *Environment) AddConstraints(cns ...simplex.Constraint) {
265 | env.lprg.AddConstraints(cns...)
266 | }
267 |
268 | func (env *Environment) FinishLayout() {
269 | if err := env.lprg.Minimize(); err != nil {
270 | log.Println(err)
271 | }
272 | for _, sheet := range env.sheets {
273 | sheet.UpdateWorld(env.lprg)
274 | }
275 | }
276 |
277 | func (env *Environment) Draw(ctx gl.Context) {
278 | select {
279 | case <-env.watchEvent:
280 | env.Load(ctx)
281 | default:
282 | }
283 |
284 | sort.Sort(byZ(env.sheets))
285 |
286 | env.indices = env.indices[:0]
287 | env.verts = env.verts[:0]
288 | env.colors = env.colors[:0]
289 | env.dists = env.dists[:0]
290 | env.texcoords = env.texcoords[:0]
291 | env.touches = env.touches[:0]
292 |
293 | for i, sheet := range env.sheets {
294 | m := sheet.M()
295 | x, y, z := m.world[0][3], m.world[1][3], m.world[2][3]
296 | w, h := m.world[0][0], m.world[1][1]
297 | r := m.Roundness
298 |
299 | n := uint32(len(env.verts)) / 4
300 |
301 | if i != 0 { // degenerate triangles
302 | // TODO make sure last v2 matches based on how shadows are being added
303 | env.indices = append(env.indices,
304 | env.indices[len(env.indices)-2], env.indices[len(env.indices)-1], env.indices[len(env.indices)-1],
305 | env.indices[len(env.indices)-1], env.indices[len(env.indices)-1], n,
306 | )
307 | }
308 |
309 | // *** shadow layer
310 | if m.BehaviorFlags&DescriptorRaised == DescriptorRaised {
311 | // (r/w) should be a value in range [0.0..0.5] given that a value of 0.5
312 | // is an ellipse/circle. mapping this range to [1..3]*z provides a decent
313 | // default for resizing ellipses shadows for visibility given current
314 | // algorithm in shader.
315 | // s := (1 + 6*(r/w)) + z // TODO this needs harder limits based on size of material
316 | s := 4 + z
317 |
318 | // min := h
319 | // if w < h {
320 | // min = w
321 | // }
322 | // _ = min
323 |
324 | // s += min / 32
325 |
326 | ss := 2 * s
327 | // TODO how should roundness scale
328 | rr := r * ((w + ss) / w)
329 | // rr += s
330 |
331 | // rr += min / 8
332 |
333 | // clamp rr for circular shadows
334 | if rr > (w+ss)/2 {
335 | rr = (w + ss) / 2
336 | }
337 | // if rr > (h+ss)/2 {
338 | // rr = (h + ss) / 2
339 | // }
340 |
341 | x -= s
342 | w += ss
343 |
344 | y -= s * 1.5 // offset shadow from material
345 | h += ss
346 |
347 | env.indices = append(env.indices,
348 | n, n+2, n+1,
349 | n, n+3, n+2,
350 | )
351 | env.verts = append(env.verts,
352 | x, y, -z, rr, // v0
353 | x, y+h, -z, rr, // v1
354 | x+w, y+h, -z, rr, // v2
355 | x+w, y, -z, rr, // v3
356 | )
357 | env.colors = append(env.colors,
358 | m.cr, m.cg, m.cb, m.ca,
359 | m.cr, m.cg, m.cb, m.ca,
360 | m.cr, m.cg, m.cb, m.ca,
361 | m.cr, m.cg, m.cb, m.ca,
362 | )
363 | env.dists = append(env.dists,
364 | 0.0, 0.0, w, h, // v0 left, bottom
365 | 0.0, 1.0, w, h, // v1 left, top
366 | 1.0, 1.0, w, h, // v2 right, top
367 | 1.0, 0.0, w, h, // v3 right, bottom
368 | )
369 | env.texcoords = append(env.texcoords,
370 | -1, -1, -1, -1,
371 | -1, -1, -1, -1,
372 | -1, -1, -1, -1,
373 | -1, -1, -1, -1,
374 | )
375 | env.touches = append(env.touches,
376 | 0, 0, 2, 0,
377 | 0, 0, 2, 0,
378 | 0, 0, 2, 0,
379 | 0, 0, 2, 0,
380 | )
381 | }
382 | // *** end shadow layer
383 |
384 | x, y, z = m.world[0][3], m.world[1][3], m.world[2][3]
385 | w, h = m.world[0][0], m.world[1][1]
386 | n = uint32(len(env.verts)) / 4
387 |
388 | // sin, cos := f32.Sin(m.Rotate), f32.Cos(m.Rotate)
389 | // fx, fy := m.world[0][0], m.world[1][1]
390 | // w = fx*cos - fy*sin
391 | // h = fx*sin + fy*cos
392 |
393 | env.indices = append(env.indices,
394 | n, n+2, n+1, n, n+3, n+2,
395 | n+2, n+7, n+6, n+2, n+3, n+7,
396 | n+7, n+3, n, n+7, n, n+4,
397 | n+4, n+6, n+7, n+4, n+5, n+6,
398 | n+6, n+1, n+2, n+6, n+5, n+1,
399 | n+1, n+5, n+4, n+1, n+4, n,
400 | )
401 | env.verts = append(env.verts,
402 | x, y, z, r,
403 | x, y+h, z, r,
404 | x+w, y+h, z, r,
405 | x+w, y, z, r,
406 | x, y, z-1, r,
407 | x, y+h, z-1, r,
408 | x+w, y+h, z-1, r,
409 | x+w, y, z-1, r,
410 | )
411 |
412 | alpha := float32(0)
413 | if m.BehaviorFlags&DescriptorRaised == DescriptorRaised {
414 | alpha = m.ca
415 | }
416 | env.colors = append(env.colors,
417 | m.cr, m.cg, m.cb, alpha,
418 | m.cr, m.cg, m.cb, alpha,
419 | m.cr, m.cg, m.cb, alpha,
420 | m.cr, m.cg, m.cb, alpha,
421 | 1, 1, 1, alpha,
422 | 1, 1, 1, alpha,
423 | 1, 1, 1, alpha,
424 | 1, 1, 1, alpha,
425 | )
426 | env.dists = append(env.dists,
427 | 0.0, 0.0, w, h, // v0 left, bottom
428 | 0.0, 1.0, w, h, // v1 left, top
429 | 1.0, 1.0, w, h, // v2 right, top
430 | 1.0, 0.0, w, h, // v3 right, bottom
431 | 0.0, 0.0, w, h, // v0 left, bottom
432 | 0.0, 1.0, w, h, // v1 left, top
433 | 1.0, 1.0, w, h, // v2 right, top
434 | 1.0, 0.0, w, h, // v3 right, bottom
435 | )
436 | env.texcoords = append(env.texcoords,
437 | -1, -1, -1, -1,
438 | -1, -1, -1, -1,
439 | -1, -1, -1, -1,
440 | -1, -1, -1, -1,
441 | -1, -1, -1, -1,
442 | -1, -1, -1, -1,
443 | -1, -1, -1, -1,
444 | -1, -1, -1, -1,
445 | )
446 |
447 | ex, ey := m.touch.x, m.touch.y
448 | es := float32(m.touch.state)
449 | ed := float32(time.Since(m.touch.start) / time.Millisecond)
450 | env.touches = append(env.touches,
451 | ex, ey, es, ed,
452 | ex, ey, es, ed,
453 | ex, ey, es, ed,
454 | ex, ey, es, ed,
455 | ex, ey, es, ed,
456 | ex, ey, es, ed,
457 | ex, ey, es, ed,
458 | ex, ey, es, ed,
459 | )
460 |
461 | if m.icon.x != -1 {
462 | n = uint32(len(env.verts)) / 4
463 | env.indices = append(env.indices,
464 | n, n+2, n+1, n, n+3, n+2,
465 | )
466 | env.verts = append(env.verts,
467 | x, y, z, 0,
468 | x, y+h, z, 0,
469 | x+w, y+h, z, 0,
470 | x+w, y, z, 0,
471 | )
472 | env.colors = append(env.colors,
473 | m.icon.r, m.icon.g, m.icon.b, m.icon.a,
474 | m.icon.r, m.icon.g, m.icon.b, m.icon.a,
475 | m.icon.r, m.icon.g, m.icon.b, m.icon.a,
476 | m.icon.r, m.icon.g, m.icon.b, m.icon.a,
477 | )
478 | env.dists = append(env.dists,
479 | 0.0, 0.0, w, h, // v0 left, bottom
480 | 0.0, 1.0, w, h, // v1 left, top
481 | 1.0, 1.0, w, h, // v2 right, top
482 | 1.0, 0.0, w, h, // v3 right, bottom
483 | )
484 | s := float32(0.0234375)
485 | ix, iy := m.icon.x, m.icon.y
486 | env.texcoords = append(env.texcoords,
487 | ix, iy+s, 1, 0,
488 | ix, iy, 1, 0,
489 | ix+s, iy, 1, 0,
490 | ix+s, iy+s, 1, 0,
491 | )
492 | env.touches = append(env.touches,
493 | 0, 0, 2, 0,
494 | 0, 0, 2, 0,
495 | 0, 0, 2, 0,
496 | 0, 0, 2, 0,
497 | )
498 | }
499 |
500 | if m.ShowImage {
501 | n = uint32(len(env.verts)) / 4
502 | env.indices = append(env.indices,
503 | n, n+2, n+1, n, n+3, n+2,
504 | )
505 | env.verts = append(env.verts,
506 | x, y, z, 0,
507 | x, y+h, z, 0,
508 | x+w, y+h, z, 0,
509 | x+w, y, z, 0,
510 | )
511 | env.colors = append(env.colors,
512 | 1, 1, 1, alpha,
513 | 1, 1, 1, alpha,
514 | 1, 1, 1, alpha,
515 | 1, 1, 1, alpha,
516 | )
517 | env.dists = append(env.dists,
518 | 0.0, 0.0, w, h, // v0 left, bottom
519 | 0.0, 1.0, w, h, // v1 left, top
520 | 1.0, 1.0, w, h, // v2 right, top
521 | 1.0, 0.0, w, h, // v3 right, bottom
522 | )
523 |
524 | // imgX, imgY = 1, 1
525 | // proportion that image occupies of actual texture
526 | // texture has to be like 2048x2048
527 | // so if image is 800x600
528 | // then max X value would be 800/2048
529 | // and max Y value would be 600/2048
530 | mX := float32(imageSize.X) / float32(imageTextureSize)
531 | mY := float32(imageSize.Y) / float32(imageTextureSize)
532 | env.texcoords = append(env.texcoords,
533 | 0, mY, 3, 0,
534 | 0, 0, 3, 0,
535 | mX, 0, 3, 0,
536 | mX, mY, 3, 0,
537 | )
538 | env.touches = append(env.touches,
539 | 0, 0, 3, 0,
540 | 0, 0, 3, 0,
541 | 0, 0, 3, 0,
542 | 0, 0, 3, 0,
543 | )
544 | }
545 |
546 | // draw text
547 | tx, ty := m.world[0][3], m.world[1][3]
548 | th := m.text.height
549 | if th == 0 {
550 | th = m.world[1][1]
551 | }
552 |
553 | pad := float32(text.Pad) * (th / text.FontSize)
554 | ty = ty + m.world[1][1] - (text.AscentUnit * th)
555 |
556 | for _, r := range m.text.value {
557 | a := text.Bounds[r]
558 | ax, ay, aw, ah, aa := a[0], a[1], a[2], a[3], a[4]
559 | ax *= th
560 | ay *= th
561 | aw *= th
562 | ah *= th
563 | aa *= th
564 |
565 | if unicode.IsSpace(r) {
566 | if r == '\n' {
567 | tx = m.world[0][3]
568 | ty -= (text.AscentUnit * th)
569 | }
570 | } else {
571 | n = uint32(len(env.verts)) / 4
572 | env.indices = append(env.indices,
573 | n, n+2, n+1, n, n+3, n+2,
574 | )
575 | env.verts = append(env.verts,
576 | tx+ax-pad, ty-ay-pad, z, 0, // v0
577 | tx+ax-pad, ty-ay+ah+pad, z, 0, // v1
578 | tx+ax+aw+pad, ty-ay+ah+pad, z, 0, // v2
579 | tx+ax+aw+pad, ty-ay-pad, z, 0, // v3
580 | )
581 | env.colors = append(env.colors,
582 | m.text.r, m.text.g, m.text.b, m.text.a,
583 | m.text.r, m.text.g, m.text.b, m.text.a,
584 | m.text.r, m.text.g, m.text.b, m.text.a,
585 | m.text.r, m.text.g, m.text.b, m.text.a,
586 | )
587 | env.dists = append(env.dists,
588 | 0.0, 0.0, aw, th,
589 | 0.0, 1.0, aw, th,
590 | 1.0, 1.0, aw, th,
591 | 1.0, 0.0, aw, th,
592 | )
593 | env.touches = append(env.touches,
594 | 0, 0, 2, 0,
595 | 0, 0, 2, 0,
596 | 0, 0, 2, 0,
597 | 0, 0, 2, 0,
598 | )
599 | g := text.Texcoords[r]
600 | gx, gy, gw, gh := g[0], g[1], g[2], g[3]
601 | env.texcoords = append(env.texcoords,
602 | gx, gy+gh, 0, 0,
603 | gx, gy, 0, 0,
604 | gx+gw, gy, 0, 0,
605 | gx+gw, gy+gh, 0, 0,
606 | )
607 | }
608 |
609 | tx += aa
610 | }
611 | }
612 |
613 | env.prg.Use(ctx)
614 | env.prg.Mat4(ctx, env.uniforms.view, env.View)
615 | env.prg.Mat4(ctx, env.uniforms.proj, env.proj)
616 | env.prg.U4f(ctx, env.uniforms.shadowColor, shdr, shdg, shdb, shda)
617 | env.prg.U4f(ctx, env.uniforms.glyphconf, text.FontSize, text.Pad, 0.5, 1)
618 |
619 | env.buffers.texcoords.Bind(ctx)
620 | env.buffers.texcoords.Update(ctx, env.texcoords)
621 | env.prg.Pointer(ctx, env.attribs.texcoord, 4)
622 |
623 | env.buffers.touches.Bind(ctx)
624 | env.buffers.touches.Update(ctx, env.touches)
625 | env.prg.Pointer(ctx, env.attribs.touch, 4)
626 |
627 | env.buffers.dists.Bind(ctx)
628 | env.buffers.dists.Update(ctx, env.dists)
629 | env.prg.Pointer(ctx, env.attribs.dist, 4)
630 |
631 | env.buffers.colors.Bind(ctx)
632 | env.buffers.colors.Update(ctx, env.colors)
633 | env.prg.Pointer(ctx, env.attribs.color, 4)
634 |
635 | env.buffers.verts.Bind(ctx)
636 | env.buffers.verts.Update(ctx, env.verts)
637 | env.buffers.indices.Bind(ctx)
638 | env.buffers.indices.Update(ctx, env.indices)
639 | env.prg.Pointer(ctx, env.attribs.vertex, 4)
640 |
641 | if env.glyphs.Value != 0 {
642 | env.glyphs.Bind(ctx, linearFilter, DefaultWrap)
643 | env.prg.U1i(ctx, env.uniforms.glyphs, int(env.glyphs.Value-1))
644 | }
645 |
646 | if env.icons.Value != 0 {
647 | env.icons.Bind(ctx, DefaultFilter, DefaultWrap)
648 | env.prg.U1i(ctx, env.uniforms.icons, int(env.icons.Value-1))
649 | }
650 |
651 | if env.image.Value != 0 {
652 | env.image.Bind(ctx, DefaultFilter, DefaultWrap)
653 | env.prg.U1i(ctx, env.uniforms.image, int(env.image.Value-1))
654 | }
655 |
656 | env.buffers.indices.Draw(ctx, env.prg, gl.TRIANGLES)
657 | }
658 |
659 | func (env *Environment) DrawGridDebug(ctx gl.Context) {
660 | env.Grid.draw(ctx, env.View, env.proj)
661 | }
662 |
663 | func (env *Environment) Touch(ev touch.Event) bool {
664 | ex, ey := ev.X, float32(windowSize.HeightPx)-ev.Y
665 | for i := len(env.sheets) - 1; i >= 0; i-- {
666 | sheet := env.sheets[i]
667 | if !sheet.Hidden() && sheet.Contains(ex, ey) {
668 | mtrl := sheet.M()
669 | mtrl.touch.state = ev.Type
670 | mtrl.touch.x, mtrl.touch.y = mtrl.RelativeCoords(ex, ey)
671 | if ev.Type == touch.TypeBegin {
672 | mtrl.touch.start = time.Now()
673 | }
674 |
675 | switch sheet := sheet.(type) {
676 | case *Button:
677 | if ev.Type == touch.TypeBegin && sheet.OnPress != nil {
678 | sheet.OnPress()
679 | }
680 | if sheet.OnTouch != nil {
681 | sheet.OnTouch(ev)
682 | }
683 | case *FloatingActionButton:
684 | if ev.Type == touch.TypeBegin && sheet.OnPress != nil {
685 | sheet.OnPress()
686 | }
687 | if sheet.OnTouch != nil {
688 | sheet.OnTouch(ev)
689 | }
690 | default:
691 | log.Printf("Unhandled type %T\n", sheet)
692 | continue
693 | }
694 | return true
695 | }
696 | }
697 | return false
698 | }
699 |
700 | func (env *Environment) NewMaterial(ctx gl.Context) *Material {
701 | m := New(ctx, Black)
702 | m.SetColor(env.plt.Light)
703 | env.sheets = append(env.sheets, m)
704 | return m
705 | }
706 |
707 | func (env *Environment) NewButton(ctx gl.Context) *Button {
708 | btn := &Button{Material: New(ctx, Black)} // TODO update constructor to remove color arg
709 | btn.SetColor(env.plt.Primary)
710 | btn.SetIconColor(White)
711 | env.sheets = append(env.sheets, btn)
712 | return btn
713 | }
714 |
715 | func (env *Environment) NewFloatingActionButton(ctx gl.Context) *FloatingActionButton {
716 | fab := &FloatingActionButton{Material: New(ctx, Black)} // TODO update constructor to remove color arg
717 | fab.SetColor(env.plt.Primary)
718 | fab.SetIconColor(White)
719 | fab.IsCircle = true
720 | env.sheets = append(env.sheets, fab)
721 | return fab
722 | }
723 |
724 | func (env *Environment) NewToolbar(ctx gl.Context) *Toolbar {
725 | bar := &Toolbar{
726 | Material: New(ctx, Black),
727 | Nav: env.NewButton(ctx),
728 | Title: env.NewMaterial(ctx),
729 | }
730 | bar.SetColor(env.plt.Light) // create specific ColorFromPalette on each type to localize selection
731 | bar.Nav.BehaviorFlags = DescriptorFlat
732 | bar.Nav.SetIcon(icon.NavigationMenu)
733 | bar.Nav.SetIconColor(Black)
734 | bar.Title.BehaviorFlags = DescriptorFlat
735 | env.sheets = append(env.sheets, bar)
736 | return bar
737 | }
738 |
739 | func (env *Environment) NewMenu(ctx gl.Context) *Menu {
740 | mu := &Menu{Material: New(ctx, Black)}
741 | mu.SetColor(env.plt.Light)
742 | mu.BehaviorFlags |= VisibilityTemporary
743 | mu.hidden = true
744 | env.sheets = append(env.sheets, mu)
745 | return mu
746 | }
747 |
--------------------------------------------------------------------------------
/text/texcoords.go:
--------------------------------------------------------------------------------
1 | // generated by gen.go; DO NOT EDIT
2 | package text
3 |
4 | const AscentUnit = 0.79671454
5 | const DescentUnit = 0.20328543
6 | const TextureSize = 512
7 | const FontSize = 52
8 | const Pad = 3
9 |
10 | var Texcoords = map[rune][4]float32{
11 | 'ý': {0, 0, 0.060791016, 0.11401367},
12 | 'þ': {0.060791016, 0, 0.057128906, 0.11328125},
13 | '|': {0.11791992, 0, 0.01928711, 0.11328125},
14 | '¦': {0.13720703, 0, 0.01928711, 0.11328125},
15 | 'j': {0.15649414, 0, 0.033691406, 0.111083984},
16 | 'ÿ': {0.19018555, 0, 0.060791016, 0.11035156},
17 | 'Ç': {0.25097656, 0, 0.063964844, 0.10961914},
18 | 'Ô': {0.3149414, 0, 0.07470703, 0.107421875},
19 | 'Ó': {0.38964844, 0, 0.07470703, 0.107421875},
20 | 'Û': {0.46435547, 0, 0.064208984, 0.107421875},
21 | 'Ò': {0.52856445, 0, 0.07470703, 0.107421875},
22 | 'Ú': {0.6032715, 0, 0.064208984, 0.107421875},
23 | 'Ù': {0.66748047, 0, 0.064208984, 0.107421875},
24 | 'Î': {0.73168945, 0, 0.044677734, 0.10644531},
25 | 'Á': {0.7763672, 0, 0.07397461, 0.10644531},
26 | 'È': {0.8503418, 0, 0.049560547, 0.10644531},
27 | 'É': {0.89990234, 0, 0.049560547, 0.10644531},
28 | 'Ý': {0, 0.11401367, 0.06567383, 0.10644531},
29 | 'Â': {0.06567383, 0.11401367, 0.07397461, 0.10644531},
30 | 'Ì': {0.13964844, 0.11401367, 0.0390625, 0.10644531},
31 | 'Ê': {0.17871094, 0.11401367, 0.049560547, 0.10644531},
32 | 'Í': {0.22827148, 0.11401367, 0.040039062, 0.10644531},
33 | 'À': {0.26831055, 0.11401367, 0.07397461, 0.10644531},
34 | 'Q': {0.34228516, 0.11401367, 0.07470703, 0.10595703},
35 | 'Õ': {0.4169922, 0.11401367, 0.07470703, 0.10424805},
36 | 'Ö': {0.49169922, 0.11401367, 0.07470703, 0.103759766},
37 | 'Ü': {0.56640625, 0.11401367, 0.064208984, 0.103759766},
38 | 'J': {0.63061523, 0.11401367, 0.03930664, 0.103515625},
39 | 'Ñ': {0.6699219, 0.11401367, 0.06616211, 0.103271484},
40 | 'Ã': {0.736084, 0.11401367, 0.07397461, 0.103271484},
41 | 'Ï': {0.8100586, 0.11401367, 0.039794922, 0.1027832},
42 | 'Ä': {0.8498535, 0.11401367, 0.07397461, 0.1027832},
43 | 'Ë': {0.9238281, 0.11401367, 0.049560547, 0.1027832},
44 | '¶': {0, 0.22045898, 0.061767578, 0.10180664},
45 | 'Å': {0.061767578, 0.22045898, 0.07397461, 0.10058594},
46 | ']': {0.13574219, 0.22045898, 0.03173828, 0.1003418},
47 | '[': {0.16748047, 0.22045898, 0.03173828, 0.1003418},
48 | '}': {0.19921875, 0.22045898, 0.041992188, 0.1003418},
49 | '(': {0.24121094, 0.22045898, 0.03540039, 0.1003418},
50 | '{': {0.27661133, 0.22045898, 0.041992188, 0.1003418},
51 | ')': {0.31860352, 0.22045898, 0.03515625, 0.1003418},
52 | 'å': {0.35375977, 0.22045898, 0.052978516, 0.095458984},
53 | '$': {0.40673828, 0.22045898, 0.05444336, 0.09472656},
54 | '@': {0.46118164, 0.22045898, 0.08886719, 0.09326172},
55 | 'Ø': {0.5500488, 0.22045898, 0.07470703, 0.091796875},
56 | 'ç': {0.62475586, 0.22045898, 0.049804688, 0.091552734},
57 | 'q': {0.67456055, 0.22045898, 0.057128906, 0.091552734},
58 | 'p': {0.73168945, 0.22045898, 0.057128906, 0.091552734},
59 | 'g': {0.78881836, 0.22045898, 0.060546875, 0.091552734},
60 | 'ð': {0.84936523, 0.22045898, 0.05908203, 0.09057617},
61 | 'ò': {0.90844727, 0.22045898, 0.05908203, 0.09057617},
62 | 'ù': {0, 0.32226562, 0.05493164, 0.09057617},
63 | 'ú': {0.05493164, 0.32226562, 0.05493164, 0.09057617},
64 | 'à': {0.10986328, 0.32226562, 0.052978516, 0.09057617},
65 | 'y': {0.1628418, 0.32226562, 0.060791016, 0.09057617},
66 | 'á': {0.22363281, 0.32226562, 0.052978516, 0.09057617},
67 | 'è': {0.27661133, 0.32226562, 0.055419922, 0.09057617},
68 | 'ê': {0.33203125, 0.32226562, 0.055419922, 0.09057617},
69 | 'ô': {0.38745117, 0.32226562, 0.05908203, 0.09057617},
70 | 'é': {0.4465332, 0.32226562, 0.055419922, 0.09057617},
71 | 'û': {0.5019531, 0.32226562, 0.05493164, 0.09057617},
72 | 'µ': {0.55688477, 0.32226562, 0.05493164, 0.09057617},
73 | 'â': {0.6118164, 0.32226562, 0.052978516, 0.09057617},
74 | 'ó': {0.6647949, 0.32226562, 0.05908203, 0.09057617},
75 | 'ß': {0.72387695, 0.32226562, 0.059570312, 0.09033203},
76 | '§': {0.78344727, 0.32226562, 0.048583984, 0.08984375},
77 | 'b': {0.83203125, 0.32226562, 0.057128906, 0.08984375},
78 | 'd': {0.88916016, 0.32226562, 0.057128906, 0.08984375},
79 | 'î': {0.94628906, 0.32226562, 0.044677734, 0.08959961},
80 | 'ì': {0, 0.4128418, 0.03125, 0.08959961},
81 | 'í': {0.03125, 0.4128418, 0.03173828, 0.08959961},
82 | 'f': {0.06298828, 0.4128418, 0.04736328, 0.08935547},
83 | 'h': {0.11035156, 0.4128418, 0.05493164, 0.08886719},
84 | 'k': {0.1652832, 0.4128418, 0.053222656, 0.08886719},
85 | 'l': {0.21850586, 0.4128418, 0.020751953, 0.08886719},
86 | 'õ': {0.23925781, 0.4128418, 0.05908203, 0.087402344},
87 | 'ã': {0.29833984, 0.4128418, 0.052978516, 0.087402344},
88 | 'ö': {0.35131836, 0.4128418, 0.05908203, 0.08691406},
89 | 'ä': {0.4104004, 0.4128418, 0.052978516, 0.08691406},
90 | 'ü': {0.4633789, 0.4128418, 0.05493164, 0.08691406},
91 | 'ë': {0.51831055, 0.4128418, 0.055419922, 0.08691406},
92 | '¿': {0.57373047, 0.4128418, 0.049804688, 0.08666992},
93 | '?': {0.62353516, 0.4128418, 0.049560547, 0.08666992},
94 | 'i': {0.6730957, 0.4128418, 0.022460938, 0.08666992},
95 | '8': {0.69555664, 0.4128418, 0.057128906, 0.08642578},
96 | 'O': {0.75268555, 0.4128418, 0.07470703, 0.08642578},
97 | '&': {0.8273926, 0.4128418, 0.075927734, 0.08642578},
98 | '0': {0.9033203, 0.4128418, 0.057861328, 0.08642578},
99 | 'ñ': {0, 0.5024414, 0.05493164, 0.08642578},
100 | '¢': {0.05493164, 0.5024414, 0.049804688, 0.08618164},
101 | '3': {0.10473633, 0.5024414, 0.057373047, 0.08618164},
102 | 'S': {0.16210938, 0.5024414, 0.0546875, 0.08618164},
103 | '©': {0.21679688, 0.5024414, 0.08642578, 0.08618164},
104 | '%': {0.30322266, 0.5024414, 0.08520508, 0.08618164},
105 | '9': {0.38842773, 0.5024414, 0.057373047, 0.08618164},
106 | 'C': {0.44580078, 0.5024414, 0.063964844, 0.08618164},
107 | '6': {0.5097656, 0.5024414, 0.057373047, 0.08618164},
108 | '®': {0.5671387, 0.5024414, 0.08642578, 0.08618164},
109 | 'G': {0.65356445, 0.5024414, 0.068359375, 0.08618164},
110 | 'ï': {0.7219238, 0.5024414, 0.039794922, 0.0859375},
111 | '!': {0.76171875, 0.5024414, 0.024414062, 0.08569336},
112 | '¡': {0.7861328, 0.5024414, 0.024414062, 0.08569336},
113 | '£': {0.8105469, 0.5024414, 0.060791016, 0.08520508},
114 | '5': {0.8713379, 0.5024414, 0.055419922, 0.08520508},
115 | 'U': {0.9267578, 0.5024414, 0.064208984, 0.08520508},
116 | '2': {0, 0.5888672, 0.056884766, 0.08520508},
117 | '¾': {0.056884766, 0.5888672, 0.084228516, 0.08520508},
118 | '4': {0.14111328, 0.5888672, 0.064453125, 0.0847168},
119 | 'A': {0.2055664, 0.5888672, 0.07397461, 0.084472656},
120 | 'W': {0.27954102, 0.5888672, 0.100097656, 0.084228516},
121 | 'F': {0.37963867, 0.5888672, 0.049316406, 0.084228516},
122 | 'E': {0.42895508, 0.5888672, 0.049560547, 0.084228516},
123 | 'D': {0.47851562, 0.5888672, 0.06542969, 0.084228516},
124 | 'H': {0.5439453, 0.5888672, 0.06347656, 0.084228516},
125 | 'B': {0.6074219, 0.5888672, 0.059326172, 0.084228516},
126 | 'I': {0.66674805, 0.5888672, 0.038085938, 0.084228516},
127 | 'M': {0.704834, 0.5888672, 0.080566406, 0.084228516},
128 | 'K': {0.7854004, 0.5888672, 0.061035156, 0.084228516},
129 | 'L': {0.84643555, 0.5888672, 0.049560547, 0.084228516},
130 | 'Ð': {0.8959961, 0.5888672, 0.07299805, 0.084228516},
131 | '7': {0, 0.67407227, 0.05834961, 0.084228516},
132 | 'N': {0.05834961, 0.67407227, 0.06616211, 0.084228516},
133 | '¥': {0.12451172, 0.67407227, 0.06542969, 0.084228516},
134 | '¼': {0.1899414, 0.67407227, 0.0793457, 0.084228516},
135 | '½': {0.2692871, 0.67407227, 0.08129883, 0.084228516},
136 | '⁄': {0.35058594, 0.67407227, 0.060302734, 0.084228516},
137 | 'Þ': {0.41088867, 0.67407227, 0.055419922, 0.084228516},
138 | 'P': {0.4663086, 0.67407227, 0.055419922, 0.084228516},
139 | 'V': {0.5217285, 0.67407227, 0.06982422, 0.084228516},
140 | 'X': {0.59155273, 0.67407227, 0.067871094, 0.084228516},
141 | 'T': {0.6594238, 0.67407227, 0.06225586, 0.084228516},
142 | '/': {0.7216797, 0.67407227, 0.047851562, 0.084228516},
143 | 'Y': {0.76953125, 0.67407227, 0.06567383, 0.084228516},
144 | 'Æ': {0.8352051, 0.67407227, 0.092285156, 0.084228516},
145 | 'Z': {0.92749023, 0.67407227, 0.05810547, 0.084228516},
146 | '#': {0, 0.7583008, 0.072265625, 0.084228516},
147 | '\\': {0.072265625, 0.7583008, 0.047851562, 0.084228516},
148 | '1': {0.12011719, 0.7583008, 0.038330078, 0.084228516},
149 | 'R': {0.15844727, 0.7583008, 0.060791016, 0.084228516},
150 | ';': {0.21923828, 0.7583008, 0.028564453, 0.08081055},
151 | 't': {0.24780273, 0.7583008, 0.04248047, 0.079833984},
152 | 'ø': {0.2902832, 0.7583008, 0.05908203, 0.07324219},
153 | '±': {0.34936523, 0.7583008, 0.057861328, 0.07080078},
154 | ':': {0.40722656, 0.7583008, 0.024414062, 0.0690918},
155 | 'o': {0.43164062, 0.7583008, 0.05908203, 0.068115234},
156 | 'e': {0.49072266, 0.7583008, 0.055419922, 0.068115234},
157 | 'c': {0.5461426, 0.7583008, 0.049804688, 0.068115234},
158 | 's': {0.59594727, 0.7583008, 0.048583984, 0.068115234},
159 | 'a': {0.64453125, 0.7583008, 0.052978516, 0.068115234},
160 | 'æ': {0.69750977, 0.7583008, 0.08666992, 0.068115234},
161 | 'r': {0.7841797, 0.7583008, 0.041748047, 0.06713867},
162 | 'n': {0.82592773, 0.7583008, 0.05493164, 0.06713867},
163 | 'm': {0.8808594, 0.7583008, 0.08618164, 0.06713867},
164 | 'u': {0, 0.8425293, 0.05493164, 0.06713867},
165 | 'x': {0.05493164, 0.8425293, 0.05883789, 0.06616211},
166 | 'v': {0.11376953, 0.8425293, 0.060791016, 0.06616211},
167 | 'w': {0.17456055, 0.8425293, 0.0859375, 0.06616211},
168 | 'ı': {0.26049805, 0.8425293, 0.020751953, 0.06616211},
169 | 'z': {0.28125, 0.8425293, 0.048339844, 0.06616211},
170 | '<': {0.32958984, 0.8425293, 0.057373047, 0.061767578},
171 | '>': {0.3869629, 0.8425293, 0.057373047, 0.061767578},
172 | '÷': {0.44433594, 0.8425293, 0.057861328, 0.05883789},
173 | '+': {0.50219727, 0.8425293, 0.057861328, 0.057861328},
174 | '*': {0.5600586, 0.8425293, 0.059814453, 0.057617188},
175 | '^': {0.61987305, 0.8425293, 0.061767578, 0.057617188},
176 | '³': {0.6816406, 0.8425293, 0.040771484, 0.056884766},
177 | '²': {0.7224121, 0.8425293, 0.040283203, 0.056152344},
178 | '¤': {0.7626953, 0.8425293, 0.055419922, 0.055419922},
179 | '¹': {0.81811523, 0.8425293, 0.030761719, 0.05517578},
180 | '×': {0.84887695, 0.8425293, 0.05419922, 0.053955078},
181 | '‹': {0.9030762, 0.8425293, 0.032958984, 0.053955078},
182 | '›': {0.93603516, 0.8425293, 0.032958984, 0.053955078},
183 | '»': {0, 0.90966797, 0.053222656, 0.053955078},
184 | '«': {0.053222656, 0.90966797, 0.053222656, 0.053955078},
185 | 'º': {0.10644531, 0.90966797, 0.041015625, 0.046142578},
186 | 'ª': {0.14746094, 0.90966797, 0.037109375, 0.046142578},
187 | '°': {0.18457031, 0.90966797, 0.04296875, 0.04296875},
188 | '=': {0.22753906, 0.90966797, 0.057373047, 0.03955078},
189 | '¬': {0.2849121, 0.90966797, 0.057373047, 0.038330078},
190 | '•': {0.34228516, 0.90966797, 0.03515625, 0.037841797},
191 | '\'': {0.3774414, 0.90966797, 0.021728516, 0.037841797},
192 | '"': {0.39916992, 0.90966797, 0.03955078, 0.037841797},
193 | '„': {0.4387207, 0.90966797, 0.04638672, 0.036621094},
194 | '‚': {0.48510742, 0.90966797, 0.02734375, 0.036621094},
195 | '“': {0.5124512, 0.90966797, 0.046142578, 0.036621094},
196 | '’': {0.55859375, 0.90966797, 0.02709961, 0.036621094},
197 | ',': {0.58569336, 0.90966797, 0.02734375, 0.036621094},
198 | '”': {0.6130371, 0.90966797, 0.046142578, 0.036621094},
199 | '‘': {0.6591797, 0.90966797, 0.02709961, 0.036621094},
200 | '¸': {0.6862793, 0.90966797, 0.030273438, 0.036132812},
201 | '˚': {0.71655273, 0.90966797, 0.034179688, 0.032958984},
202 | '´': {0.7507324, 0.90966797, 0.03125, 0.028076172},
203 | 'ˆ': {0.7819824, 0.90966797, 0.044677734, 0.028076172},
204 | '`': {0.82666016, 0.90966797, 0.03125, 0.028076172},
205 | '~': {0.85791016, 0.90966797, 0.057373047, 0.025634766},
206 | '·': {0.9152832, 0.90966797, 0.024414062, 0.025390625},
207 | '.': {0.93969727, 0.90966797, 0.024414062, 0.025390625},
208 | '˜': {0, 0.96362305, 0.04736328, 0.025146484},
209 | '¨': {0.04736328, 0.96362305, 0.039794922, 0.021972656},
210 | '—': {0.0871582, 0.96362305, 0.10498047, 0.020263672},
211 | '–': {0.19213867, 0.96362305, 0.05419922, 0.020263672},
212 | '': {0.24633789, 0.96362305, 0.036376953, 0.020263672},
213 | '-': {0.28271484, 0.96362305, 0.036376953, 0.020263672},
214 | '¯': {0.3190918, 0.96362305, 0.06298828, 0.018798828},
215 | '_': {0.38208008, 0.96362305, 0.053955078, 0.018798828},
216 | ' ': {0.43603516, 0.96362305, 0.01171875, 0.01171875},
217 | ' ': {0.4477539, 0.96362305, 0.01171875, 0.01171875},
218 | }
219 |
220 | var Bounds = map[rune][5]float32{
221 | 'ý': {0.0024038462, 0.24038461, 0.48317307, 1.0072116, 0.48798078},
222 | 'þ': {0.084134616, 0.24038461, 0.4471154, 1, 0.58653843},
223 | '|': {0.23798077, 0.24038461, 0.07451923, 1, 0.5504808},
224 | '¦': {0.23798077, 0.24038461, 0.07451923, 1, 0.5504808},
225 | 'j': {-0.033653848, 0.24038461, 0.21634616, 0.97836536, 0.2596154},
226 | 'ÿ': {0.0024038462, 0.24038461, 0.48317307, 0.97115386, 0.48798078},
227 | 'Ç': {0.060096152, 0.24038461, 0.5144231, 0.9639423, 0.60336536},
228 | 'Ô': {0.060096152, 0.009615385, 0.6201923, 0.9423077, 0.74278843},
229 | 'Ó': {0.060096152, 0.009615385, 0.6201923, 0.9423077, 0.74278843},
230 | 'Û': {0.088942304, 0.009615385, 0.5168269, 0.9423077, 0.69711536},
231 | 'Ò': {0.060096152, 0.009615385, 0.6201923, 0.9423077, 0.74278843},
232 | 'Ú': {0.088942304, 0.009615385, 0.5168269, 0.9423077, 0.69711536},
233 | 'Ù': {0.088942304, 0.009615385, 0.5168269, 0.9423077, 0.69711536},
234 | 'Î': {0.009615385, 0, 0.32451922, 0.9326923, 0.33894232},
235 | 'Á': {-0.0024038462, 0, 0.6129808, 0.9326923, 0.6081731},
236 | 'È': {0.09615385, 0, 0.37259614, 0.9326923, 0.52884614},
237 | 'É': {0.09615385, 0, 0.37259614, 0.9326923, 0.52884614},
238 | 'Ý': {-0.0024038462, 0, 0.53125, 0.9326923, 0.5264423},
239 | 'Â': {-0.0024038462, 0, 0.6129808, 0.9326923, 0.6081731},
240 | 'Ì': {0.03125, 0, 0.26923078, 0.9326923, 0.33894232},
241 | 'Ê': {0.09615385, 0, 0.37259614, 0.9326923, 0.52884614},
242 | 'Í': {0.040865384, 0, 0.27884614, 0.9326923, 0.33894232},
243 | 'À': {-0.0024038462, 0, 0.6129808, 0.9326923, 0.6081731},
244 | 'Q': {0.060096152, 0.20192307, 0.6201923, 0.92788464, 0.74038464},
245 | 'Õ': {0.060096152, 0.009615385, 0.6201923, 0.9110577, 0.74278843},
246 | 'Ö': {0.060096152, 0.009615385, 0.6201923, 0.90625, 0.74278843},
247 | 'Ü': {0.088942304, 0.009615385, 0.5168269, 0.90625, 0.69711536},
248 | 'J': {-0.09134615, 0.18990384, 0.2716346, 0.90384614, 0.2716346},
249 | 'Ñ': {0.09615385, 0, 0.5360577, 0.9014423, 0.72836536},
250 | 'Ã': {-0.0024038462, 0, 0.6129808, 0.9014423, 0.6081731},
251 | 'Ï': {0.033653848, 0, 0.27644232, 0.89663464, 0.33894232},
252 | 'Ä': {-0.0024038462, 0, 0.6129808, 0.89663464, 0.6081731},
253 | 'Ë': {0.09615385, 0, 0.37259614, 0.89663464, 0.52884614},
254 | '¶': {0.05528846, 0.12740384, 0.49278846, 0.8870192, 0.65384614},
255 | 'Å': {-0.0024038462, 0, 0.6129808, 0.875, 0.6081731},
256 | ']': {0.026442308, 0.15865384, 0.19711539, 0.87259614, 0.3028846},
257 | '[': {0.07932692, 0.15865384, 0.19711539, 0.87259614, 0.3028846},
258 | '}': {0.026442308, 0.15865384, 0.29807693, 0.87259614, 0.3533654},
259 | '(': {0.040865384, 0.15865384, 0.23317307, 0.87259614, 0.30048078},
260 | '{': {0.028846154, 0.15865384, 0.29807693, 0.87259614, 0.3533654},
261 | ')': {0.03125, 0.15865384, 0.23076923, 0.87259614, 0.30048078},
262 | 'å': {0.045673076, 0.009615385, 0.40625, 0.8245192, 0.53125},
263 | '$': {0.060096152, 0.057692308, 0.42067307, 0.8173077, 0.5504808},
264 | '@': {0.052884616, 0.088942304, 0.75961536, 0.80288464, 0.86538464},
265 | 'Ø': {0.060096152, 0.03846154, 0.6201923, 0.78846157, 0.74278843},
266 | 'ç': {0.05528846, 0.24038461, 0.375, 0.7860577, 0.46394232},
267 | 'q': {0.05528846, 0.24038461, 0.4471154, 0.7860577, 0.58653843},
268 | 'p': {0.084134616, 0.24038461, 0.4471154, 0.7860577, 0.58653843},
269 | 'g': {0.01923077, 0.24038461, 0.48076922, 0.7860577, 0.5192308},
270 | 'ð': {0.05528846, 0.009615385, 0.46634614, 0.7764423, 0.5769231},
271 | 'ò': {0.05528846, 0.009615385, 0.46634614, 0.7764423, 0.5769231},
272 | 'ù': {0.07932692, 0.009615385, 0.42548078, 0.7764423, 0.5889423},
273 | 'ú': {0.07932692, 0.009615385, 0.42548078, 0.7764423, 0.5889423},
274 | 'à': {0.045673076, 0.009615385, 0.40625, 0.7764423, 0.53125},
275 | 'y': {0.0024038462, 0.24038461, 0.48317307, 0.7764423, 0.48798078},
276 | 'á': {0.045673076, 0.009615385, 0.40625, 0.7764423, 0.53125},
277 | 'è': {0.05528846, 0.009615385, 0.43028846, 0.7764423, 0.5360577},
278 | 'ê': {0.05528846, 0.009615385, 0.43028846, 0.7764423, 0.5360577},
279 | 'ô': {0.05528846, 0.009615385, 0.46634614, 0.7764423, 0.5769231},
280 | 'é': {0.05528846, 0.009615385, 0.43028846, 0.7764423, 0.5360577},
281 | 'û': {0.07932692, 0.009615385, 0.42548078, 0.7764423, 0.5889423},
282 | 'µ': {0.084134616, 0.24038461, 0.42548078, 0.7764423, 0.59375},
283 | 'â': {0.045673076, 0.009615385, 0.40625, 0.7764423, 0.53125},
284 | 'ó': {0.05528846, 0.009615385, 0.46634614, 0.7764423, 0.5769231},
285 | 'ß': {0.084134616, 0.009615385, 0.47115386, 0.77403843, 0.60096157},
286 | '§': {0.057692308, 0.0048076925, 0.36298078, 0.7692308, 0.48557693},
287 | 'b': {0.084134616, 0.009615385, 0.4471154, 0.7692308, 0.58653843},
288 | 'd': {0.05528846, 0.009615385, 0.4471154, 0.7692308, 0.58653843},
289 | 'î': {-0.033653848, 0, 0.32451922, 0.7668269, 0.2596154},
290 | 'ì': {-0.016826924, 0, 0.1923077, 0.7668269, 0.2596154},
291 | 'í': {0.084134616, 0, 0.19711539, 0.7668269, 0.2596154},
292 | 'f': {0.014423077, 0, 0.35096154, 0.7644231, 0.32932693},
293 | 'h': {0.084134616, 0, 0.42548078, 0.75961536, 0.5889423},
294 | 'k': {0.084134616, 0, 0.40865386, 0.75961536, 0.49519232},
295 | 'l': {0.084134616, 0, 0.088942304, 0.75961536, 0.2596154},
296 | 'õ': {0.05528846, 0.009615385, 0.46634614, 0.7451923, 0.5769231},
297 | 'ã': {0.045673076, 0.009615385, 0.40625, 0.7451923, 0.53125},
298 | 'ö': {0.05528846, 0.009615385, 0.46634614, 0.74038464, 0.5769231},
299 | 'ä': {0.045673076, 0.009615385, 0.40625, 0.74038464, 0.53125},
300 | 'ü': {0.07932692, 0.009615385, 0.42548078, 0.74038464, 0.5889423},
301 | 'ë': {0.05528846, 0.009615385, 0.43028846, 0.74038464, 0.5360577},
302 | '¿': {0.033653848, 0.1923077, 0.375, 0.7379808, 0.42548078},
303 | '?': {0.01923077, 0.014423077, 0.37259614, 0.7379808, 0.42548078},
304 | 'i': {0.07692308, 0, 0.10576923, 0.7379808, 0.2596154},
305 | '8': {0.052884616, 0.009615385, 0.4471154, 0.7355769, 0.5504808},
306 | 'O': {0.060096152, 0.009615385, 0.6201923, 0.7355769, 0.74278843},
307 | '&': {0.052884616, 0.009615385, 0.63221157, 0.7355769, 0.7019231},
308 | '0': {0.048076924, 0.009615385, 0.45432693, 0.7355769, 0.5504808},
309 | 'ñ': {0.084134616, 0, 0.42548078, 0.7355769, 0.5889423},
310 | '¢': {0.09134615, 0.009615385, 0.375, 0.7331731, 0.5504808},
311 | '3': {0.040865384, 0.009615385, 0.44951922, 0.7331731, 0.5504808},
312 | 'S': {0.050480768, 0.009615385, 0.42307693, 0.7331731, 0.5192308},
313 | '©': {0.048076924, 0.009615385, 0.7355769, 0.7331731, 0.8317308},
314 | '%': {0.050480768, 0.009615385, 0.7235577, 0.7331731, 0.8245192},
315 | '9': {0.052884616, 0.009615385, 0.44951922, 0.7331731, 0.5504808},
316 | 'C': {0.060096152, 0.009615385, 0.5144231, 0.7331731, 0.60336536},
317 | '6': {0.05528846, 0.009615385, 0.44951922, 0.7331731, 0.5504808},
318 | '®': {0.048076924, 0.009615385, 0.7355769, 0.7331731, 0.8317308},
319 | 'G': {0.060096152, 0.009615385, 0.5576923, 0.7331731, 0.68990386},
320 | 'ï': {-0.009615385, 0, 0.27644232, 0.7307692, 0.2596154},
321 | '!': {0.072115384, 0.014423077, 0.125, 0.72836536, 0.26923078},
322 | '¡': {0.072115384, 0.1826923, 0.125, 0.72836536, 0.26923078},
323 | '£': {0.033653848, 0, 0.48317307, 0.7235577, 0.5504808},
324 | '5': {0.06490385, 0.009615385, 0.43028846, 0.7235577, 0.5504808},
325 | 'U': {0.088942304, 0.009615385, 0.5168269, 0.7235577, 0.69711536},
326 | '2': {0.048076924, 0, 0.44471154, 0.7235577, 0.5504808},
327 | '¾': {0.014423077, 0, 0.7139423, 0.7235577, 0.7379808},
328 | '4': {0.012019231, 0, 0.5192308, 0.71875, 0.5504808},
329 | 'A': {-0.0024038462, 0, 0.6129808, 0.71634614, 0.6081731},
330 | 'W': {0.0072115385, 0, 0.8701923, 0.7139423, 0.88461536},
331 | 'F': {0.09615385, 0, 0.37019232, 0.7139423, 0.4903846},
332 | 'E': {0.09615385, 0, 0.37259614, 0.7139423, 0.52884614},
333 | 'D': {0.09615385, 0, 0.52884614, 0.7139423, 0.68509614},
334 | 'H': {0.09615385, 0, 0.50961536, 0.7139423, 0.7019231},
335 | 'B': {0.09615385, 0, 0.46875, 0.7139423, 0.6201923},
336 | 'I': {0.040865384, 0, 0.2596154, 0.7139423, 0.33894232},
337 | 'M': {0.09615385, 0, 0.67788464, 0.7139423, 0.8701923},
338 | 'K': {0.09615385, 0, 0.48557693, 0.7139423, 0.5793269},
339 | 'L': {0.09615385, 0, 0.37259614, 0.7139423, 0.4903846},
340 | 'Ð': {0.021634616, 0, 0.60336536, 0.7139423, 0.68509614},
341 | '7': {0.043269232, 0, 0.4591346, 0.7139423, 0.5504808},
342 | 'N': {0.09615385, 0, 0.5360577, 0.7139423, 0.72836536},
343 | '¥': {0.012019231, 0, 0.52884614, 0.7139423, 0.5504808},
344 | '¼': {0.028846154, 0, 0.66586536, 0.7139423, 0.7379808},
345 | '½': {0.01923077, 0, 0.68509614, 0.7139423, 0.7379808},
346 | '⁄': {-0.17548077, 0, 0.4783654, 0.7139423, 0.1298077},
347 | 'Þ': {0.09615385, 0, 0.43028846, 0.7139423, 0.5769231},
348 | 'P': {0.09615385, 0, 0.43028846, 0.7139423, 0.5769231},
349 | 'V': {-0.0024038462, 0, 0.57211536, 0.7139423, 0.5673077},
350 | 'X': {-0.0024038462, 0, 0.55288464, 0.7139423, 0.5480769},
351 | 'T': {0.009615385, 0, 0.49759614, 0.7139423, 0.5192308},
352 | '/': {0.0072115385, 0, 0.35576922, 0.7139423, 0.37259614},
353 | 'Y': {-0.0024038462, 0, 0.53125, 0.7139423, 0.5264423},
354 | 'Æ': {-0.0024038462, 0, 0.7932692, 0.7139423, 0.85096157},
355 | 'Z': {0.040865384, 0, 0.45673078, 0.7139423, 0.53846157},
356 | '#': {0.024038462, 0, 0.59615386, 0.7139423, 0.64663464},
357 | '\\': {0.009615385, 0, 0.35576922, 0.7139423, 0.37259614},
358 | '1': {0.086538464, 0, 0.26201922, 0.7139423, 0.5504808},
359 | 'R': {0.09615385, 0, 0.48317307, 0.7139423, 0.5889423},
360 | ';': {0.03125, 0.1298077, 0.16586539, 0.68028843, 0.26923078},
361 | 't': {0.016826924, 0.009615385, 0.3028846, 0.6706731, 0.33894232},
362 | 'ø': {0.05528846, 0.036057692, 0.46634614, 0.6057692, 0.5769231},
363 | '±': {0.048076924, 0, 0.45432693, 0.5817308, 0.5504808},
364 | ':': {0.072115384, 0.014423077, 0.125, 0.56490386, 0.26923078},
365 | 'o': {0.05528846, 0.009615385, 0.46634614, 0.55528843, 0.5769231},
366 | 'e': {0.05528846, 0.009615385, 0.43028846, 0.55528843, 0.5360577},
367 | 'c': {0.05528846, 0.009615385, 0.375, 0.55528843, 0.46394232},
368 | 's': {0.043269232, 0.009615385, 0.36298078, 0.55528843, 0.45192307},
369 | 'a': {0.045673076, 0.009615385, 0.40625, 0.55528843, 0.53125},
370 | 'æ': {0.045673076, 0.009615385, 0.7379808, 0.55528843, 0.83413464},
371 | 'r': {0.084134616, 0, 0.29567307, 0.5456731, 0.39903846},
372 | 'n': {0.084134616, 0, 0.42548078, 0.5456731, 0.5889423},
373 | 'm': {0.084134616, 0, 0.7331731, 0.5456731, 0.89663464},
374 | 'u': {0.07932692, 0.009615385, 0.42548078, 0.5456731, 0.5889423},
375 | 'x': {0.016826924, 0, 0.46394232, 0.5360577, 0.5},
376 | 'v': {-0.0024038462, 0, 0.48317307, 0.5360577, 0.4783654},
377 | 'w': {0.0072115385, 0, 0.7307692, 0.5360577, 0.7451923},
378 | 'ı': {0.084134616, 0, 0.088942304, 0.5360577, 0.2596154},
379 | 'z': {0.040865384, 0, 0.36057693, 0.5360577, 0.43990386},
380 | '<': {0.050480768, -0.115384616, 0.44951922, 0.49278846, 0.5504808},
381 | '>': {0.050480768, -0.115384616, 0.44951922, 0.49278846, 0.5504808},
382 | '÷': {0.048076924, -0.120192304, 0.45432693, 0.46394232, 0.5504808},
383 | '+': {0.048076924, -0.12740384, 0.45432693, 0.45432693, 0.5504808},
384 | '*': {0.03846154, -0.30769232, 0.47355768, 0.45192307, 0.5504808},
385 | '^': {0.01923077, -0.26682693, 0.49278846, 0.45192307, 0.53125},
386 | '³': {0.014423077, -0.27884614, 0.28605768, 0.44471154, 0.33173078},
387 | '²': {0.024038462, -0.28605768, 0.28125, 0.4375, 0.33173078},
388 | '¤': {0.060096152, -0.13701923, 0.43028846, 0.43028846, 0.5504808},
389 | '¹': {0.028846154, -0.28605768, 0.1875, 0.4278846, 0.33173078},
390 | '×': {0.067307696, -0.14423077, 0.41826922, 0.4158654, 0.5504808},
391 | '‹': {0.03846154, -0.05528846, 0.20913461, 0.4158654, 0.28846154},
392 | '›': {0.040865384, -0.05528846, 0.20913461, 0.4158654, 0.28846154},
393 | '»': {0.040865384, -0.05528846, 0.40865386, 0.4158654, 0.48798078},
394 | '«': {0.03846154, -0.05528846, 0.40865386, 0.4158654, 0.48798078},
395 | 'º': {0.03125, -0.38221154, 0.28846154, 0.33894232, 0.35096154},
396 | 'ª': {0.033653848, -0.38221154, 0.25, 0.33894232, 0.33173078},
397 | '°': {0.060096152, -0.4158654, 0.30769232, 0.30769232, 0.4278846},
398 | '=': {0.050480768, -0.2139423, 0.44951922, 0.27403846, 0.5504808},
399 | '¬': {0.050480768, -0.12740384, 0.44951922, 0.26201922, 0.5504808},
400 | '•': {0.072115384, -0.23557693, 0.23076923, 0.25721154, 0.375},
401 | '\'': {0.06490385, -0.45673078, 0.098557696, 0.25721154, 0.22596154},
402 | '"': {0.06490385, -0.45673078, 0.27403846, 0.25721154, 0.40144232},
403 | '„': {0.03125, 0.1298077, 0.34134614, 0.2451923, 0.4375},
404 | '‚': {0.03125, 0.1298077, 0.15384616, 0.2451923, 0.25},
405 | '“': {0.012019231, -0.46875, 0.33894232, 0.2451923, 0.36298078},
406 | '’': {0.012019231, -0.46875, 0.1514423, 0.2451923, 0.17548077},
407 | ',': {0.03125, 0.1298077, 0.15384616, 0.2451923, 0.25},
408 | '”': {0.012019231, -0.46875, 0.33894232, 0.2451923, 0.36298078},
409 | '‘': {0.012019231, -0.46875, 0.1514423, 0.2451923, 0.17548077},
410 | '¸': {0.016826924, 0.24038461, 0.1826923, 0.24038461, 0.20432693},
411 | '˚': {0.17788461, -0.6057692, 0.22115384, 0.20913461, 0.5769231},
412 | '´': {0.1923077, -0.6057692, 0.1923077, 0.1610577, 0.5769231},
413 | 'ˆ': {0.125, -0.6057692, 0.32451922, 0.1610577, 0.5769231},
414 | '`': {0.1923077, -0.6057692, 0.1923077, 0.1610577, 0.5769231},
415 | '~': {0.050480768, -0.28365386, 0.44951922, 0.13701923, 0.5504808},
416 | '·': {0.072115384, -0.28605768, 0.125, 0.13461539, 0.26923078},
417 | '.': {0.072115384, 0.014423077, 0.125, 0.13461539, 0.26923078},
418 | '˜': {0.125, -0.60336536, 0.35096154, 0.13221154, 0.5769231},
419 | '¨': {0.14903846, -0.6298077, 0.27644232, 0.100961536, 0.5769231},
420 | '—': {0.040865384, -0.22596154, 0.9182692, 0.084134616, 1},
421 | '–': {0.040865384, -0.22596154, 0.41826922, 0.084134616, 0.5},
422 | '': {0.040865384, -0.22596154, 0.24278846, 0.084134616, 0.3221154},
423 | '-': {0.040865384, -0.22596154, 0.24278846, 0.084134616, 0.3221154},
424 | '¯': {-0.0024038462, -0.75961536, 0.5048077, 0.069711536, 0.5},
425 | '_': {-0.0024038462, 0.15865384, 0.4158654, 0.069711536, 0.41105768},
426 | ' ': {0, 0, 0, 0, 0.2596154},
427 | ' ': {0, 0, 0, 0, 0.2596154},
428 | }
429 |
430 |
--------------------------------------------------------------------------------
/icon/icon_mdpi.go:
--------------------------------------------------------------------------------
1 | // generated by makeicons.go; DO NOT EDIT
2 | package icon
3 |
4 | type Icon int
5 |
6 | func (ic Icon) Texcoords() (float32, float32) {
7 | tc := texcoords[int(ic)]
8 | return tc[0], tc[1]
9 | }
10 |
11 | const (
12 | Action3dRotation Icon = iota
13 | ActionAccessibility
14 | ActionAccessible
15 | ActionAccountBalance
16 | ActionAccountBalanceWallet
17 | ActionAccountBox
18 | ActionAccountCircle
19 | ActionAddShoppingCart
20 | ActionAlarm
21 | ActionAlarmAdd
22 | ActionAlarmOff
23 | ActionAlarmOn
24 | ActionAllOut
25 | ActionAndroid
26 | ActionAnnouncement
27 | ActionAspectRatio
28 | ActionAssessment
29 | ActionAssignment
30 | ActionAssignmentInd
31 | ActionAssignmentLate
32 | ActionAssignmentReturn
33 | ActionAssignmentReturned
34 | ActionAssignmentTurnedIn
35 | ActionAutorenew
36 | ActionBackup
37 | ActionBook
38 | ActionBookmark
39 | ActionBookmarkBorder
40 | ActionBugReport
41 | ActionBuild
42 | ActionCached
43 | ActionCameraEnhance
44 | ActionCardGiftcard
45 | ActionCardMembership
46 | ActionCardTravel
47 | ActionChangeHistory
48 | ActionCheckCircle
49 | ActionChromeReaderMode
50 | ActionClass
51 | ActionCode
52 | ActionCompareArrows
53 | ActionCopyright
54 | ActionCreditCard
55 | ActionDashboard
56 | ActionDateRange
57 | ActionDelete
58 | ActionDescription
59 | ActionDns
60 | ActionDone
61 | ActionDoneAll
62 | ActionDonutLarge
63 | ActionDonutSmall
64 | ActionEject
65 | ActionEvent
66 | ActionEventSeat
67 | ActionExitToApp
68 | ActionExplore
69 | ActionExtension
70 | ActionFace
71 | ActionFavorite
72 | ActionFavoriteBorder
73 | ActionFeedback
74 | ActionFindInPage
75 | ActionFindReplace
76 | ActionFingerprint
77 | ActionFlightLand
78 | ActionFlightTakeoff
79 | ActionFlipToBack
80 | ActionFlipToFront
81 | ActionGavel
82 | ActionGetApp
83 | ActionGif
84 | ActionGrade
85 | ActionGroupWork
86 | ActionHelp
87 | ActionHelpOutline
88 | ActionHighlightOff
89 | ActionHistory
90 | ActionHome
91 | ActionHourglassEmpty
92 | ActionHourglassFull
93 | ActionHttp
94 | ActionHttps
95 | ActionImportantDevices
96 | ActionInfo
97 | ActionInfoOutline
98 | ActionInput
99 | ActionInvertColors
100 | ActionLabel
101 | ActionLabelOutline
102 | ActionLanguage
103 | ActionLaunch
104 | ActionLightbulbOutline
105 | ActionLineStyle
106 | ActionLineWeight
107 | ActionList
108 | ActionLock
109 | ActionLockOpen
110 | ActionLockOutline
111 | ActionLoyalty
112 | ActionMarkunreadMailbox
113 | ActionMotorcycle
114 | ActionNoteAdd
115 | ActionOfflinePin
116 | ActionOpacity
117 | ActionOpenInBrowser
118 | ActionOpenInNew
119 | ActionOpenWith
120 | ActionPageview
121 | ActionPanTool
122 | ActionPayment
123 | ActionPermCameraMic
124 | ActionPermContactCalendar
125 | ActionPermDataSetting
126 | ActionPermDeviceInformation
127 | ActionPermIdentity
128 | ActionPermMedia
129 | ActionPermPhoneMsg
130 | ActionPermScanWifi
131 | ActionPets
132 | ActionPictureInPicture
133 | ActionPictureInPictureAlt
134 | ActionPlayForWork
135 | ActionPolymer
136 | ActionPowerSettingsNew
137 | ActionPregnantWoman
138 | ActionPrint
139 | ActionQueryBuilder
140 | ActionQuestionAnswer
141 | ActionReceipt
142 | ActionRecordVoiceOver
143 | ActionRedeem
144 | ActionReorder
145 | ActionReportProblem
146 | ActionRestore
147 | ActionRoom
148 | ActionRoundedCorner
149 | ActionRowing
150 | ActionSchedule
151 | ActionSearch
152 | ActionSettings
153 | ActionSettingsApplications
154 | ActionSettingsBackupRestore
155 | ActionSettingsBluetooth
156 | ActionSettingsBrightness
157 | ActionSettingsCell
158 | ActionSettingsEthernet
159 | ActionSettingsInputAntenna
160 | ActionSettingsInputComponent
161 | ActionSettingsInputComposite
162 | ActionSettingsInputHdmi
163 | ActionSettingsInputSvideo
164 | ActionSettingsOverscan
165 | ActionSettingsPhone
166 | ActionSettingsPower
167 | ActionSettingsRemote
168 | ActionSettingsVoice
169 | ActionShop
170 | ActionShopTwo
171 | ActionShoppingBasket
172 | ActionShoppingCart
173 | ActionSpeakerNotes
174 | ActionSpellcheck
175 | ActionStars
176 | ActionStore
177 | ActionSubject
178 | ActionSupervisorAccount
179 | ActionSwapHoriz
180 | ActionSwapVert
181 | ActionSwapVerticalCircle
182 | ActionSystemUpdateAlt
183 | ActionTab
184 | ActionTabUnselected
185 | ActionTheaters
186 | ActionThumbDown
187 | ActionThumbUp
188 | ActionThumbsUpDown
189 | ActionTimeline
190 | ActionToc
191 | ActionToday
192 | ActionToll
193 | ActionTouchApp
194 | ActionTrackChanges
195 | ActionTranslate
196 | ActionTrendingDown
197 | ActionTrendingFlat
198 | ActionTrendingUp
199 | ActionTurnedIn
200 | ActionTurnedInNot
201 | ActionUpdate
202 | ActionVerifiedUser
203 | ActionViewAgenda
204 | ActionViewArray
205 | ActionViewCarousel
206 | ActionViewColumn
207 | ActionViewDay
208 | ActionViewHeadline
209 | ActionViewList
210 | ActionViewModule
211 | ActionViewQuilt
212 | ActionViewStream
213 | ActionViewWeek
214 | ActionVisibility
215 | ActionVisibilityOff
216 | ActionWatchLater
217 | ActionWork
218 | ActionYoutubeSearchedFor
219 | ActionZoomIn
220 | ActionZoomOut
221 | AlertAddAlert
222 | AlertError
223 | AlertErrorOutline
224 | AlertWarning
225 | AvAddToQueue
226 | AvAirplay
227 | AvAlbum
228 | AvArtTrack
229 | AvAvTimer
230 | AvClosedCaption
231 | AvEqualizer
232 | AvExplicit
233 | AvFastForward
234 | AvFastRewind
235 | AvFiberDvr
236 | AvFiberManualRecord
237 | AvFiberNew
238 | AvFiberPin
239 | AvFiberSmartRecord
240 | AvForward10
241 | AvForward30
242 | AvForward5
243 | AvGames
244 | AvHd
245 | AvHearing
246 | AvHighQuality
247 | AvLibraryAdd
248 | AvLibraryBooks
249 | AvLibraryMusic
250 | AvLoop
251 | AvMic
252 | AvMicNone
253 | AvMicOff
254 | AvMovie
255 | AvMusicVideo
256 | AvNewReleases
257 | AvNotInterested
258 | AvPause
259 | AvPauseCircleFilled
260 | AvPauseCircleOutline
261 | AvPlayArrow
262 | AvPlayCircleFilled
263 | AvPlayCircleOutline
264 | AvPlaylistAdd
265 | AvPlaylistAddCheck
266 | AvPlaylistPlay
267 | AvQueue
268 | AvQueueMusic
269 | AvQueuePlayNext
270 | AvRadio
271 | AvRecentActors
272 | AvRemoveFromQueue
273 | AvRepeat
274 | AvRepeatOne
275 | AvReplay
276 | AvReplay10
277 | AvReplay30
278 | AvReplay5
279 | AvShuffle
280 | AvSkipNext
281 | AvSkipPrevious
282 | AvSlowMotionVideo
283 | AvSnooze
284 | AvSortByAlpha
285 | AvStop
286 | AvSubscriptions
287 | AvSubtitles
288 | AvSurroundSound
289 | AvVideoLibrary
290 | AvVideocam
291 | AvVideocamOff
292 | AvVolumeDown
293 | AvVolumeMute
294 | AvVolumeOff
295 | AvVolumeUp
296 | AvWeb
297 | AvWebAsset
298 | CommunicationBusiness
299 | CommunicationCall
300 | CommunicationCallEnd
301 | CommunicationCallMade
302 | CommunicationCallMerge
303 | CommunicationCallMissed
304 | CommunicationCallMissedOutgoing
305 | CommunicationCallReceived
306 | CommunicationCallSplit
307 | CommunicationChat
308 | CommunicationChatBubble
309 | CommunicationChatBubbleOutline
310 | CommunicationClearAll
311 | CommunicationComment
312 | CommunicationContactMail
313 | CommunicationContactPhone
314 | CommunicationContacts
315 | CommunicationDialerSip
316 | CommunicationDialpad
317 | CommunicationEmail
318 | CommunicationForum
319 | CommunicationImportContacts
320 | CommunicationImportExport
321 | CommunicationInvertColorsOff
322 | CommunicationLiveHelp
323 | CommunicationLocationOff
324 | CommunicationLocationOn
325 | CommunicationMailOutline
326 | CommunicationMessage
327 | CommunicationNoSim
328 | CommunicationPhone
329 | CommunicationPhonelinkErase
330 | CommunicationPhonelinkLock
331 | CommunicationPhonelinkRing
332 | CommunicationPhonelinkSetup
333 | CommunicationPortableWifiOff
334 | CommunicationPresentToAll
335 | CommunicationRingVolume
336 | CommunicationScreenShare
337 | CommunicationSpeakerPhone
338 | CommunicationStayCurrentLandscape
339 | CommunicationStayCurrentPortrait
340 | CommunicationStayPrimaryLandscape
341 | CommunicationStayPrimaryPortrait
342 | CommunicationStopScreenShare
343 | CommunicationSwapCalls
344 | CommunicationTextsms
345 | CommunicationVoicemail
346 | CommunicationVpnKey
347 | ContentAdd
348 | ContentAddBox
349 | ContentAddCircle
350 | ContentAddCircleOutline
351 | ContentArchive
352 | ContentBackspace
353 | ContentBlock
354 | ContentClear
355 | ContentContentCopy
356 | ContentContentCut
357 | ContentContentPaste
358 | ContentCreate
359 | ContentDrafts
360 | ContentFilterList
361 | ContentFlag
362 | ContentFontDownload
363 | ContentForward
364 | ContentGesture
365 | ContentInbox
366 | ContentLink
367 | ContentMail
368 | ContentMarkunread
369 | ContentMoveToInbox
370 | ContentNextWeek
371 | ContentRedo
372 | ContentRemove
373 | ContentRemoveCircle
374 | ContentRemoveCircleOutline
375 | ContentReply
376 | ContentReplyAll
377 | ContentReport
378 | ContentSave
379 | ContentSelectAll
380 | ContentSend
381 | ContentSort
382 | ContentTextFormat
383 | ContentUnarchive
384 | ContentUndo
385 | ContentWeekend
386 | DeviceAccessAlarm
387 | DeviceAccessAlarms
388 | DeviceAccessTime
389 | DeviceAddAlarm
390 | DeviceAirplanemodeActive
391 | DeviceAirplanemodeInactive
392 | DeviceBattery20
393 | DeviceBattery30
394 | DeviceBattery50
395 | DeviceBattery60
396 | DeviceBattery80
397 | DeviceBattery90
398 | DeviceBatteryAlert
399 | DeviceBatteryCharging20
400 | DeviceBatteryCharging30
401 | DeviceBatteryCharging50
402 | DeviceBatteryCharging60
403 | DeviceBatteryCharging80
404 | DeviceBatteryCharging90
405 | DeviceBatteryChargingFull
406 | DeviceBatteryFull
407 | DeviceBatteryStd
408 | DeviceBatteryUnknown
409 | DeviceBluetooth
410 | DeviceBluetoothConnected
411 | DeviceBluetoothDisabled
412 | DeviceBluetoothSearching
413 | DeviceBrightnessAuto
414 | DeviceBrightnessHigh
415 | DeviceBrightnessLow
416 | DeviceBrightnessMedium
417 | DeviceDataUsage
418 | DeviceDeveloperMode
419 | DeviceDevices
420 | DeviceDvr
421 | DeviceGpsFixed
422 | DeviceGpsNotFixed
423 | DeviceGpsOff
424 | DeviceGraphicEq
425 | DeviceLocationDisabled
426 | DeviceLocationSearching
427 | DeviceNetworkCell
428 | DeviceNetworkWifi
429 | DeviceNfc
430 | DeviceScreenLockLandscape
431 | DeviceScreenLockPortrait
432 | DeviceScreenLockRotation
433 | DeviceScreenRotation
434 | DeviceSdStorage
435 | DeviceSettingsSystemDaydream
436 | DeviceSignalCellular0Bar
437 | DeviceSignalCellular1Bar
438 | DeviceSignalCellular2Bar
439 | DeviceSignalCellular3Bar
440 | DeviceSignalCellular4Bar
441 | DeviceSignalCellularConnectedNoInternet0Bar
442 | DeviceSignalCellularConnectedNoInternet1Bar
443 | DeviceSignalCellularConnectedNoInternet2Bar
444 | DeviceSignalCellularConnectedNoInternet3Bar
445 | DeviceSignalCellularConnectedNoInternet4Bar
446 | DeviceSignalCellularNoSim
447 | DeviceSignalCellularNull
448 | DeviceSignalCellularOff
449 | DeviceSignalWifi0Bar
450 | DeviceSignalWifi1Bar
451 | DeviceSignalWifi1BarLock
452 | DeviceSignalWifi2Bar
453 | DeviceSignalWifi2BarLock
454 | DeviceSignalWifi3Bar
455 | DeviceSignalWifi3BarLock
456 | DeviceSignalWifi4Bar
457 | DeviceSignalWifi4BarLock
458 | DeviceSignalWifiOff
459 | DeviceStorage
460 | DeviceUsb
461 | DeviceWallpaper
462 | DeviceWidgets
463 | DeviceWifiLock
464 | DeviceWifiTethering
465 | EditorAttachFile
466 | EditorAttachMoney
467 | EditorBorderAll
468 | EditorBorderBottom
469 | EditorBorderClear
470 | EditorBorderColor
471 | EditorBorderHorizontal
472 | EditorBorderInner
473 | EditorBorderLeft
474 | EditorBorderOuter
475 | EditorBorderRight
476 | EditorBorderStyle
477 | EditorBorderTop
478 | EditorBorderVertical
479 | EditorDragHandle
480 | EditorFormatAlignCenter
481 | EditorFormatAlignJustify
482 | EditorFormatAlignLeft
483 | EditorFormatAlignRight
484 | EditorFormatBold
485 | EditorFormatClear
486 | EditorFormatColorFill
487 | EditorFormatColorReset
488 | EditorFormatColorText
489 | EditorFormatIndentDecrease
490 | EditorFormatIndentIncrease
491 | EditorFormatItalic
492 | EditorFormatLineSpacing
493 | EditorFormatListBulleted
494 | EditorFormatListNumbered
495 | EditorFormatPaint
496 | EditorFormatQuote
497 | EditorFormatShapes
498 | EditorFormatSize
499 | EditorFormatStrikethrough
500 | EditorFormatTextdirectionLToR
501 | EditorFormatTextdirectionRToL
502 | EditorFormatUnderlined
503 | EditorFunctions
504 | EditorHighlight
505 | EditorInsertChart
506 | EditorInsertComment
507 | EditorInsertDriveFile
508 | EditorInsertEmoticon
509 | EditorInsertInvitation
510 | EditorInsertLink
511 | EditorInsertPhoto
512 | EditorLinearScale
513 | EditorMergeType
514 | EditorModeComment
515 | EditorModeEdit
516 | EditorMoneyOff
517 | EditorPublish
518 | EditorShortText
519 | EditorSpaceBar
520 | EditorStrikethroughS
521 | EditorTextFields
522 | EditorVerticalAlignBottom
523 | EditorVerticalAlignCenter
524 | EditorVerticalAlignTop
525 | EditorWrapText
526 | FileAttachment
527 | FileCloud
528 | FileCloudCircle
529 | FileCloudDone
530 | FileCloudDownload
531 | FileCloudOff
532 | FileCloudQueue
533 | FileCloudUpload
534 | FileCreateNewFolder
535 | FileFileDownload
536 | FileFileUpload
537 | FileFolder
538 | FileFolderOpen
539 | FileFolderShared
540 | HardwareCast
541 | HardwareCastConnected
542 | HardwareComputer
543 | HardwareDesktopMac
544 | HardwareDesktopWindows
545 | HardwareDeveloperBoard
546 | HardwareDeviceHub
547 | HardwareDevicesOther
548 | HardwareDock
549 | HardwareGamepad
550 | HardwareHeadset
551 | HardwareHeadsetMic
552 | HardwareKeyboard
553 | HardwareKeyboardArrowDown
554 | HardwareKeyboardArrowLeft
555 | HardwareKeyboardArrowRight
556 | HardwareKeyboardArrowUp
557 | HardwareKeyboardBackspace
558 | HardwareKeyboardCapslock
559 | HardwareKeyboardHide
560 | HardwareKeyboardReturn
561 | HardwareKeyboardTab
562 | HardwareKeyboardVoice
563 | HardwareLaptop
564 | HardwareLaptopChromebook
565 | HardwareLaptopMac
566 | HardwareLaptopWindows
567 | HardwareMemory
568 | HardwareMouse
569 | HardwarePhoneAndroid
570 | HardwarePhoneIphone
571 | HardwarePhonelink
572 | HardwarePhonelinkOff
573 | HardwarePowerInput
574 | HardwareRouter
575 | HardwareScanner
576 | HardwareSecurity
577 | HardwareSimCard
578 | HardwareSmartphone
579 | HardwareSpeaker
580 | HardwareSpeakerGroup
581 | HardwareTablet
582 | HardwareTabletAndroid
583 | HardwareTabletMac
584 | HardwareToys
585 | HardwareTv
586 | HardwareVideogameAsset
587 | HardwareWatch
588 | ImageAddAPhoto
589 | ImageAddToPhotos
590 | ImageAdjust
591 | ImageAssistant
592 | ImageAssistantPhoto
593 | ImageAudiotrack
594 | ImageBlurCircular
595 | ImageBlurLinear
596 | ImageBlurOff
597 | ImageBlurOn
598 | ImageBrightness1
599 | ImageBrightness2
600 | ImageBrightness3
601 | ImageBrightness4
602 | ImageBrightness5
603 | ImageBrightness6
604 | ImageBrightness7
605 | ImageBrokenImage
606 | ImageBrush
607 | ImageCamera
608 | ImageCameraAlt
609 | ImageCameraFront
610 | ImageCameraRear
611 | ImageCameraRoll
612 | ImageCenterFocusStrong
613 | ImageCenterFocusWeak
614 | ImageCollections
615 | ImageCollectionsBookmark
616 | ImageColorLens
617 | ImageColorize
618 | ImageCompare
619 | ImageControlPoint
620 | ImageControlPointDuplicate
621 | ImageCrop
622 | ImageCrop169
623 | ImageCrop32
624 | ImageCrop54
625 | ImageCrop75
626 | ImageCropDin
627 | ImageCropFree
628 | ImageCropLandscape
629 | ImageCropOriginal
630 | ImageCropPortrait
631 | ImageCropRotate
632 | ImageCropSquare
633 | ImageDehaze
634 | ImageDetails
635 | ImageEdit
636 | ImageExposure
637 | ImageExposureNeg1
638 | ImageExposureNeg2
639 | ImageExposurePlus1
640 | ImageExposurePlus2
641 | ImageExposureZero
642 | ImageFilter
643 | ImageFilter1
644 | ImageFilter2
645 | ImageFilter3
646 | ImageFilter4
647 | ImageFilter5
648 | ImageFilter6
649 | ImageFilter7
650 | ImageFilter8
651 | ImageFilter9
652 | ImageFilter9Plus
653 | ImageFilterBAndW
654 | ImageFilterCenterFocus
655 | ImageFilterDrama
656 | ImageFilterFrames
657 | ImageFilterHdr
658 | ImageFilterNone
659 | ImageFilterTiltShift
660 | ImageFilterVintage
661 | ImageFlare
662 | ImageFlashAuto
663 | ImageFlashOff
664 | ImageFlashOn
665 | ImageFlip
666 | ImageGradient
667 | ImageGrain
668 | ImageGridOff
669 | ImageGridOn
670 | ImageHdrOff
671 | ImageHdrOn
672 | ImageHdrStrong
673 | ImageHdrWeak
674 | ImageHealing
675 | ImageImage
676 | ImageImageAspectRatio
677 | ImageIso
678 | ImageLandscape
679 | ImageLeakAdd
680 | ImageLeakRemove
681 | ImageLens
682 | ImageLinkedCamera
683 | ImageLooks
684 | ImageLooks3
685 | ImageLooks4
686 | ImageLooks5
687 | ImageLooks6
688 | ImageLooksOne
689 | ImageLooksTwo
690 | ImageLoupe
691 | ImageMonochromePhotos
692 | ImageMovieCreation
693 | ImageMovieFilter
694 | ImageMusicNote
695 | ImageNature
696 | ImageNaturePeople
697 | ImageNavigateBefore
698 | ImageNavigateNext
699 | ImagePalette
700 | ImagePanorama
701 | ImagePanoramaFishEye
702 | ImagePanoramaHorizontal
703 | ImagePanoramaVertical
704 | ImagePanoramaWideAngle
705 | ImagePhoto
706 | ImagePhotoAlbum
707 | ImagePhotoCamera
708 | ImagePhotoFilter
709 | ImagePhotoLibrary
710 | ImagePhotoSizeSelectActual
711 | ImagePhotoSizeSelectLarge
712 | ImagePhotoSizeSelectSmall
713 | ImagePictureAsPdf
714 | ImagePortrait
715 | ImageRemoveRedEye
716 | ImageRotate90DegreesCcw
717 | ImageRotateLeft
718 | ImageRotateRight
719 | ImageSlideshow
720 | ImageStraighten
721 | ImageStyle
722 | ImageSwitchCamera
723 | ImageSwitchVideo
724 | ImageTagFaces
725 | ImageTexture
726 | ImageTimelapse
727 | ImageTimer
728 | ImageTimer10
729 | ImageTimer3
730 | ImageTimerOff
731 | ImageTonality
732 | ImageTransform
733 | ImageTune
734 | ImageViewComfy
735 | ImageViewCompact
736 | ImageVignette
737 | ImageWbAuto
738 | ImageWbCloudy
739 | ImageWbIncandescent
740 | ImageWbIridescent
741 | ImageWbSunny
742 | MapsAddLocation
743 | MapsBeenhere
744 | MapsDirections
745 | MapsDirectionsBike
746 | MapsDirectionsBoat
747 | MapsDirectionsBus
748 | MapsDirectionsCar
749 | MapsDirectionsRailway
750 | MapsDirectionsRun
751 | MapsDirectionsSubway
752 | MapsDirectionsTransit
753 | MapsDirectionsWalk
754 | MapsEditLocation
755 | MapsFlight
756 | MapsHotel
757 | MapsLayers
758 | MapsLayersClear
759 | MapsLocalActivity
760 | MapsLocalAirport
761 | MapsLocalAtm
762 | MapsLocalBar
763 | MapsLocalCafe
764 | MapsLocalCarWash
765 | MapsLocalConvenienceStore
766 | MapsLocalDining
767 | MapsLocalDrink
768 | MapsLocalFlorist
769 | MapsLocalGasStation
770 | MapsLocalGroceryStore
771 | MapsLocalHospital
772 | MapsLocalHotel
773 | MapsLocalLaundryService
774 | MapsLocalLibrary
775 | MapsLocalMall
776 | MapsLocalMovies
777 | MapsLocalOffer
778 | MapsLocalParking
779 | MapsLocalPharmacy
780 | MapsLocalPhone
781 | MapsLocalPizza
782 | MapsLocalPlay
783 | MapsLocalPostOffice
784 | MapsLocalPrintshop
785 | MapsLocalSee
786 | MapsLocalShipping
787 | MapsLocalTaxi
788 | MapsMap
789 | MapsMyLocation
790 | MapsNavigation
791 | MapsNearMe
792 | MapsPersonPin
793 | MapsPersonPinCircle
794 | MapsPinDrop
795 | MapsPlace
796 | MapsRateReview
797 | MapsRestaurantMenu
798 | MapsSatellite
799 | MapsStoreMallDirectory
800 | MapsTerrain
801 | MapsTraffic
802 | MapsZoomOutMap
803 | NavigationApps
804 | NavigationArrowBack
805 | NavigationArrowDownward
806 | NavigationArrowDropDown
807 | NavigationArrowDropDownCircle
808 | NavigationArrowDropUp
809 | NavigationArrowForward
810 | NavigationArrowUpward
811 | NavigationCancel
812 | NavigationCheck
813 | NavigationChevronLeft
814 | NavigationChevronRight
815 | NavigationClose
816 | NavigationExpandLess
817 | NavigationExpandMore
818 | NavigationFullscreen
819 | NavigationFullscreenExit
820 | NavigationMenu
821 | NavigationMoreHoriz
822 | NavigationMoreVert
823 | NavigationRefresh
824 | NavigationSubdirectoryArrowLeft
825 | NavigationSubdirectoryArrowRight
826 | NavigationUnfoldLess
827 | NavigationUnfoldMore
828 | NotificationAdb
829 | NotificationAirlineSeatFlat
830 | NotificationAirlineSeatFlatAngled
831 | NotificationAirlineSeatIndividualSuite
832 | NotificationAirlineSeatLegroomExtra
833 | NotificationAirlineSeatLegroomNormal
834 | NotificationAirlineSeatLegroomReduced
835 | NotificationAirlineSeatReclineExtra
836 | NotificationAirlineSeatReclineNormal
837 | NotificationBluetoothAudio
838 | NotificationConfirmationNumber
839 | NotificationDiscFull
840 | NotificationDoNotDisturb
841 | NotificationDoNotDisturbAlt
842 | NotificationDriveEta
843 | NotificationEnhancedEncryption
844 | NotificationEventAvailable
845 | NotificationEventBusy
846 | NotificationEventNote
847 | NotificationFolderSpecial
848 | NotificationLiveTv
849 | NotificationMms
850 | NotificationMore
851 | NotificationNetworkCheck
852 | NotificationNetworkLocked
853 | NotificationNoEncryption
854 | NotificationOndemandVideo
855 | NotificationPersonalVideo
856 | NotificationPhoneBluetoothSpeaker
857 | NotificationPhoneForwarded
858 | NotificationPhoneInTalk
859 | NotificationPhoneLocked
860 | NotificationPhoneMissed
861 | NotificationPhonePaused
862 | NotificationPower
863 | NotificationRvHookup
864 | NotificationSdCard
865 | NotificationSimCardAlert
866 | NotificationSms
867 | NotificationSmsFailed
868 | NotificationSync
869 | NotificationSyncDisabled
870 | NotificationSyncProblem
871 | NotificationSystemUpdate
872 | NotificationTapAndPlay
873 | NotificationTimeToLeave
874 | NotificationVibration
875 | NotificationVoiceChat
876 | NotificationVpnLock
877 | NotificationWc
878 | NotificationWifi
879 | PlacesAcUnit
880 | PlacesAirportShuttle
881 | PlacesAllInclusive
882 | PlacesBeachAccess
883 | PlacesBusinessCenter
884 | PlacesCasino
885 | PlacesChildCare
886 | PlacesChildFriendly
887 | PlacesFitnessCenter
888 | PlacesFreeBreakfast
889 | PlacesGolfCourse
890 | PlacesHotTub
891 | PlacesKitchen
892 | PlacesPool
893 | PlacesRoomService
894 | PlacesSmokeFree
895 | PlacesSmokingRooms
896 | PlacesSpa
897 | SocialCake
898 | SocialDomain
899 | SocialGroup
900 | SocialGroupAdd
901 | SocialLocationCity
902 | SocialMood
903 | SocialMoodBad
904 | SocialNotifications
905 | SocialNotificationsActive
906 | SocialNotificationsNone
907 | SocialNotificationsOff
908 | SocialNotificationsPaused
909 | SocialPages
910 | SocialPartyMode
911 | SocialPeople
912 | SocialPeopleOutline
913 | SocialPerson
914 | SocialPersonAdd
915 | SocialPersonOutline
916 | SocialPlusOne
917 | SocialPoll
918 | SocialPublic
919 | SocialSchool
920 | SocialShare
921 | SocialWhatshot
922 | ToggleStar
923 | ToggleStarBorder
924 | ToggleStarHalf
925 | )
926 |
927 | var texcoords = [][2]float32{
928 | {0.2109375, 0.28125},{0.234375, 0.28125},{0.2578125, 0.28125},{0.28125, 0.28125},{0.3046875, 0},{0.3046875, 0.0234375},{0.3046875, 0.046875},{0.3046875, 0.0703125},{0.3046875, 0.1171875},{0.3046875, 0.09375},{0.3046875, 0.140625},{0.3046875, 0.1640625},{0.3046875, 0.1875},{0.3046875, 0.2109375},{0.3046875, 0.234375},{0.3046875, 0.2578125},{0.3046875, 0.28125},{0, 0.3046875},{0.0234375, 0.3046875},{0.046875, 0.3046875},{0.0703125, 0.3046875},{0.09375, 0.3046875},{0.1171875, 0.3046875},{0.140625, 0.3046875},{0.1640625, 0.3046875},{0.1875, 0.3046875},{0.2109375, 0.3046875},{0.234375, 0.3046875},{0.2578125, 0.3046875},{0.28125, 0.3046875},{0.3046875, 0.3046875},{0.328125, 0},{0.328125, 0.0234375},{0.328125, 0.046875},{0.328125, 0.0703125},{0.328125, 0.09375},{0.328125, 0.1171875},{0.328125, 0.140625},{0.328125, 0.1640625},{0.328125, 0.1875},{0.328125, 0.2109375},{0.328125, 0.234375},{0.328125, 0.2578125},{0.328125, 0.28125},{0.328125, 0.3046875},{0, 0.328125},{0.0234375, 0.328125},{0.046875, 0.328125},{0.09375, 0.328125},{0.0703125, 0.328125},{0.1171875, 0.328125},{0.140625, 0.328125},{0.1640625, 0.328125},{0.1875, 0.328125},{0.2109375, 0.328125},{0.234375, 0.328125},{0.2578125, 0.328125},{0.28125, 0.328125},{0.3046875, 0.328125},{0.328125, 0.328125},{0.3515625, 0},{0.3515625, 0.0234375},{0.3515625, 0.046875},{0.3515625, 0.0703125},{0.3515625, 0.09375},{0.3515625, 0.1171875},{0.3515625, 0.140625},{0.3515625, 0.1640625},{0.3515625, 0.1875},{0.3515625, 0.2109375},{0.3515625, 0.234375},{0.3515625, 0.2578125},{0.3515625, 0.28125},{0.3515625, 0.3046875},{0.3515625, 0.328125},{0, 0.3515625},{0.0234375, 0.3515625},{0.046875, 0.3515625},{0.0703125, 0.3515625},{0.09375, 0.3515625},{0.1171875, 0.3515625},{0.140625, 0.3515625},{0.1640625, 0.3515625},{0.1875, 0.3515625},{0.2109375, 0.3515625},{0.234375, 0.3515625},{0.2578125, 0.3515625},{0.28125, 0.3515625},{0.3046875, 0.3515625},{0.328125, 0.3515625},{0.3515625, 0.3515625},{0.375, 0},{0.375, 0.0234375},{0.375, 0.046875},{0.375, 0.0703125},{0.375, 0.09375},{0.375, 0.1171875},{0.375, 0.140625},{0.375, 0.1640625},{0.375, 0.1875},{0.375, 0.2109375},{0.375, 0.234375},{0.375, 0.2578125},{0.375, 0.28125},{0.375, 0.3046875},{0.375, 0.328125},{0.375, 0.3515625},{0, 0.375},{0.0234375, 0.375},{0.046875, 0.375},{0.0703125, 0.375},{0.09375, 0.375},{0.1171875, 0.375},{0.140625, 0.375},{0.1640625, 0.375},{0.1875, 0.375},{0.2109375, 0.375},{0.234375, 0.375},{0.2578125, 0.375},{0.28125, 0.375},{0.328125, 0.375},{0.3046875, 0.375},{0.3515625, 0.375},{0.375, 0.375},{0.3984375, 0},{0.3984375, 0.0234375},{0.3984375, 0.046875},{0.3984375, 0.0703125},{0.3984375, 0.09375},{0.3984375, 0.1171875},{0.3984375, 0.140625},{0.3984375, 0.1640625},{0.3984375, 0.1875},{0.3984375, 0.2109375},{0.3984375, 0.234375},{0.3984375, 0.2578125},{0.3984375, 0.28125},{0.3984375, 0.3046875},{0.3984375, 0.328125},{0.3984375, 0.3515625},{0.0234375, 0.3984375},{0.3984375, 0.375},{0, 0.3984375},{0.046875, 0.3984375},{0.0703125, 0.3984375},{0.09375, 0.3984375},{0.1171875, 0.3984375},{0.140625, 0.3984375},{0.1640625, 0.3984375},{0.1875, 0.3984375},{0.2109375, 0.3984375},{0.234375, 0.3984375},{0.2578125, 0.3984375},{0.28125, 0.3984375},{0.3046875, 0.3984375},{0.328125, 0.3984375},{0.3515625, 0.3984375},{0.375, 0.3984375},{0.3984375, 0.3984375},{0.421875, 0},{0.421875, 0.0234375},{0.421875, 0.046875},{0.421875, 0.0703125},{0.421875, 0.09375},{0.421875, 0.1171875},{0.421875, 0.140625},{0.421875, 0.1640625},{0.421875, 0.1875},{0.421875, 0.2109375},{0.421875, 0.234375},{0.421875, 0.2578125},{0.421875, 0.28125},{0.421875, 0.3046875},{0.421875, 0.328125},{0.421875, 0.3515625},{0.421875, 0.375},{0.421875, 0.3984375},{0, 0.421875},{0.0234375, 0.421875},{0.046875, 0.421875},{0.0703125, 0.421875},{0.09375, 0.421875},{0.1171875, 0.421875},{0.140625, 0.421875},{0.1640625, 0.421875},{0.1875, 0.421875},{0.2109375, 0.421875},{0.234375, 0.421875},{0.2578125, 0.421875},{0.28125, 0.421875},{0.3046875, 0.421875},{0.328125, 0.421875},{0.3515625, 0.421875},{0.375, 0.421875},{0.3984375, 0.421875},{0.421875, 0.421875},{0.4453125, 0},{0.4453125, 0.0234375},{0.4453125, 0.046875},{0.4453125, 0.0703125},{0.4453125, 0.09375},{0.4453125, 0.1171875},{0.4453125, 0.140625},{0.4453125, 0.1640625},{0.4453125, 0.1875},{0.4453125, 0.2109375},{0.4453125, 0.234375},{0.4453125, 0.2578125},{0.4453125, 0.28125},{0.0234375, 0},{0.4921875, 0.3515625},{0, 0.0234375},{0.0234375, 0.0234375},{0.046875, 0},{0.046875, 0.0234375},{0, 0.046875},{0.0234375, 0.046875},{0.046875, 0.046875},{0.0703125, 0},{0.0703125, 0.0234375},{0.0703125, 0.046875},{0, 0.0703125},{0.0234375, 0.0703125},{0.046875, 0.0703125},{0.0703125, 0.0703125},{0.09375, 0},{0.09375, 0.0234375},{0.09375, 0.046875},{0.09375, 0.0703125},{0, 0.09375},{0.0234375, 0.09375},{0.046875, 0.09375},{0.0703125, 0.09375},{0.09375, 0.09375},{0.1171875, 0},{0.1171875, 0.0234375},{0.1171875, 0.046875},{0.1171875, 0.0703125},{0.1171875, 0.09375},{0, 0.1171875},{0.0234375, 0.1171875},{0.046875, 0.1171875},{0.0703125, 0.1171875},{0.09375, 0.1171875},{0.1171875, 0.1171875},{0.140625, 0},{0.140625, 0.0234375},{0.140625, 0.046875},{0.140625, 0.0703125},{0.140625, 0.09375},{0.140625, 0.1171875},{0, 0.140625},{0.0234375, 0.140625},{0.046875, 0.140625},{0.0703125, 0.140625},{0.09375, 0.140625},{0.1171875, 0.140625},{0.140625, 0.140625},{0.1640625, 0},{0.1640625, 0.0234375},{0.1640625, 0.046875},{0.1640625, 0.0703125},{0.1640625, 0.09375},{0.0234375, 0.1640625},{0.1640625, 0.1171875},{0.1640625, 0.140625},{0, 0.1640625},{0.046875, 0.1640625},{0.0703125, 0.1640625},{0.09375, 0.1640625},{0.1171875, 0.1640625},{0.140625, 0.1640625},{0.1640625, 0.1640625},{0.1875, 0},{0.1875, 0.0234375},{0.1875, 0.046875},{0.1875, 0.0703125},{0.1875, 0.09375},{0.1875, 0.1171875},{0.1875, 0.140625},{0.1875, 0.1640625},{0, 0.1875},{0.0234375, 0.1875},{0.046875, 0.1875},{0.09375, 0.1875},{0.0703125, 0.1875},{0.1171875, 0.1875},{0.140625, 0.1875},{0.1640625, 0.1875},{0.1875, 0.1875},{0.2109375, 0},{0.2109375, 0.0234375},{0.2109375, 0.046875},{0.2109375, 0.0703125},{0.2109375, 0.09375},{0.2109375, 0.1171875},{0.2109375, 0.140625},{0.2109375, 0.1640625},{0.2109375, 0.1875},{0, 0.2109375},{0.0234375, 0.2109375},{0.046875, 0.2109375},{0.0703125, 0.2109375},{0.09375, 0.2109375},{0.1171875, 0.2109375},{0.140625, 0.2109375},{0.1640625, 0.2109375},{0.1875, 0.2109375},{0.2109375, 0.2109375},{0.234375, 0},{0.234375, 0.0234375},{0.234375, 0.046875},{0.234375, 0.0703125},{0.234375, 0.09375},{0.234375, 0.1171875},{0.234375, 0.140625},{0.234375, 0.1640625},{0.234375, 0.1875},{0.234375, 0.2109375},{0, 0.234375},{0.0234375, 0.234375},{0.046875, 0.234375},{0.0703125, 0.234375},{0.09375, 0.234375},{0.1171875, 0.234375},{0.140625, 0.234375},{0.1640625, 0.234375},{0.1875, 0.234375},{0.2109375, 0.234375},{0.234375, 0.234375},{0.2578125, 0},{0.2578125, 0.0234375},{0.2578125, 0.046875},{0.2578125, 0.0703125},{0.2578125, 0.09375},{0.2578125, 0.1171875},{0.2578125, 0.140625},{0.2578125, 0.1640625},{0.2578125, 0.1875},{0.2578125, 0.2109375},{0.2578125, 0.234375},{0, 0.2578125},{0.0234375, 0.2578125},{0.046875, 0.2578125},{0.0703125, 0.2578125},{0.09375, 0.2578125},{0.1171875, 0.2578125},{0.140625, 0.2578125},{0.1640625, 0.2578125},{0.1875, 0.2578125},{0.2109375, 0.2578125},{0.234375, 0.2578125},{0.2578125, 0.2578125},{0.28125, 0},{0.28125, 0.0234375},{0.28125, 0.046875},{0.28125, 0.0703125},{0.28125, 0.09375},{0.28125, 0.1171875},{0.28125, 0.140625},{0.28125, 0.1640625},{0.28125, 0.1875},{0.28125, 0.2109375},{0.28125, 0.2578125},{0.28125, 0.234375},{0, 0.28125},{0.0234375, 0.28125},{0.046875, 0.28125},{0.0703125, 0.28125},{0.09375, 0.28125},{0.1171875, 0.28125},{0.140625, 0.28125},{0.1640625, 0.28125},{0.1875, 0.28125},{0.4453125, 0.3046875},{0.4453125, 0.328125},{0.4453125, 0.3515625},{0.4453125, 0.375},{0.4453125, 0.3984375},{0.4453125, 0.421875},{0, 0.4453125},{0.0234375, 0.4453125},{0.046875, 0.4453125},{0.0703125, 0.4453125},{0.09375, 0.4453125},{0.1171875, 0.4453125},{0.140625, 0.4453125},{0.1640625, 0.4453125},{0.1875, 0.4453125},{0.2109375, 0.4453125},{0.234375, 0.4453125},{0.2578125, 0.4453125},{0.28125, 0.4453125},{0.3046875, 0.4453125},{0.328125, 0.4453125},{0.3515625, 0.4453125},{0.375, 0.4453125},{0.3984375, 0.4453125},{0.421875, 0.4453125},{0.4453125, 0.4453125},{0.46875, 0},{0.46875, 0.0234375},{0.46875, 0.046875},{0.46875, 0.0703125},{0.46875, 0.09375},{0.46875, 0.1171875},{0.46875, 0.140625},{0.46875, 0.1640625},{0.46875, 0.1875},{0.46875, 0.2109375},{0.46875, 0.234375},{0.46875, 0.2578125},{0.46875, 0.28125},{0.46875, 0.3046875},{0.46875, 0.328125},{0.46875, 0.3515625},{0.46875, 0.375},{0.46875, 0.3984375},{0.46875, 0.421875},{0.46875, 0.4453125},{0, 0.46875},{0.0234375, 0.46875},{0.046875, 0.46875},{0.0703125, 0.46875},{0.09375, 0.46875},{0.1171875, 0.46875},{0.140625, 0.46875},{0.1640625, 0.46875},{0.1875, 0.46875},{0.2109375, 0.46875},{0.234375, 0.46875},{0.2578125, 0.46875},{0.28125, 0.46875},{0.3046875, 0.46875},{0.328125, 0.46875},{0.3515625, 0.46875},{0.375, 0.46875},{0.3984375, 0.46875},{0.421875, 0.46875},{0.4453125, 0.46875},{0.46875, 0.46875},{0.4921875, 0},{0.4921875, 0.0234375},{0.4921875, 0.046875},{0.4921875, 0.0703125},{0.4921875, 0.09375},{0.4921875, 0.1171875},{0.4921875, 0.140625},{0.4921875, 0.1640625},{0.4921875, 0.1875},{0.4921875, 0.2109375},{0.4921875, 0.234375},{0.4921875, 0.2578125},{0.4921875, 0.28125},{0.4921875, 0.3046875},{0.4921875, 0.328125},{0, 0},{0.4921875, 0.375},{0.4921875, 0.3984375},{0.4921875, 0.421875},{0.4921875, 0.4453125},{0.4921875, 0.46875},{0, 0.4921875},{0.0234375, 0.4921875},{0.046875, 0.4921875},{0.0703125, 0.4921875},{0.09375, 0.4921875},{0.1171875, 0.4921875},{0.140625, 0.4921875},{0.1640625, 0.4921875},{0.1875, 0.4921875},{0.2109375, 0.4921875},{0.234375, 0.4921875},{0.2578125, 0.4921875},{0.28125, 0.4921875},{0.3046875, 0.4921875},{0.328125, 0.4921875},{0.3515625, 0.4921875},{0.375, 0.4921875},{0.3984375, 0.4921875},{0.421875, 0.4921875},{0.4453125, 0.4921875},{0.46875, 0.4921875},{0.4921875, 0.4921875},{0.515625, 0},{0.515625, 0.0234375},{0.515625, 0.046875},{0.515625, 0.0703125},{0.515625, 0.09375},{0.515625, 0.1171875},{0.515625, 0.140625},{0.515625, 0.1640625},{0.515625, 0.1875},{0.515625, 0.2109375},{0.515625, 0.234375},{0.515625, 0.2578125},{0.515625, 0.28125},{0.515625, 0.3046875},{0.515625, 0.328125},{0.515625, 0.3515625},{0.515625, 0.375},{0.515625, 0.3984375},{0.515625, 0.421875},{0.515625, 0.4453125},{0.515625, 0.46875},{0.515625, 0.4921875},{0, 0.515625},{0.0234375, 0.515625},{0.046875, 0.515625},{0.0703125, 0.515625},{0.09375, 0.515625},{0.1171875, 0.515625},{0.140625, 0.515625},{0.1640625, 0.515625},{0.1875, 0.515625},{0.2109375, 0.515625},{0.234375, 0.515625},{0.2578125, 0.515625},{0.28125, 0.515625},{0.3046875, 0.515625},{0.328125, 0.515625},{0.3515625, 0.515625},{0.375, 0.515625},{0.3984375, 0.515625},{0.421875, 0.515625},{0.4453125, 0.515625},{0.46875, 0.515625},{0.4921875, 0.515625},{0.515625, 0.515625},{0.5390625, 0},{0.5390625, 0.0234375},{0.5390625, 0.046875},{0.5390625, 0.0703125},{0.5390625, 0.09375},{0.5390625, 0.1171875},{0.5390625, 0.140625},{0.5390625, 0.1640625},{0.5390625, 0.1875},{0.5390625, 0.2109375},{0.5390625, 0.234375},{0.5390625, 0.375},{0.5390625, 0.2578125},{0.5390625, 0.28125},{0.5390625, 0.3046875},{0.5390625, 0.328125},{0.5390625, 0.3515625},{0.5390625, 0.3984375},{0.5390625, 0.421875},{0.5390625, 0.4453125},{0.5390625, 0.46875},{0.5390625, 0.4921875},{0.5390625, 0.515625},{0, 0.5390625},{0.0234375, 0.5390625},{0.046875, 0.5390625},{0.0703125, 0.5390625},{0.09375, 0.5390625},{0.1171875, 0.5390625},{0.140625, 0.5390625},{0.1640625, 0.5390625},{0.1875, 0.5390625},{0.2109375, 0.5390625},{0.234375, 0.5390625},{0.2578125, 0.5390625},{0.28125, 0.5390625},{0.3046875, 0.5390625},{0.328125, 0.5390625},{0.3515625, 0.5390625},{0.375, 0.5390625},{0.421875, 0.5390625},{0.3984375, 0.5390625},{0.4453125, 0.5390625},{0.46875, 0.5390625},{0.4921875, 0.5390625},{0.515625, 0.5390625},{0.5390625, 0.5390625},{0.28125, 0.5859375},{0.3046875, 0.5859375},{0.328125, 0.5859375},{0.3515625, 0.5859375},{0.375, 0.5859375},{0.3984375, 0.5859375},{0.421875, 0.5859375},{0.4453125, 0.5859375},{0.46875, 0.5859375},{0.4921875, 0.5859375},{0.515625, 0.5859375},{0.5390625, 0.5859375},{0.5625, 0.5859375},{0.5859375, 0.5859375},{0.609375, 0},{0.609375, 0.0234375},{0.609375, 0.046875},{0.609375, 0.0703125},{0.609375, 0.09375},{0.609375, 0.140625},{0.609375, 0.1171875},{0.609375, 0.1640625},{0.609375, 0.1875},{0.609375, 0.2109375},{0.609375, 0.234375},{0.609375, 0.2578125},{0.609375, 0.28125},{0.609375, 0.3046875},{0.609375, 0.328125},{0.609375, 0.3515625},{0.609375, 0.375},{0.609375, 0.3984375},{0.609375, 0.421875},{0.609375, 0.5390625},{0.609375, 0.4453125},{0.609375, 0.46875},{0.609375, 0.4921875},{0.609375, 0.515625},{0.609375, 0.5625},{0.609375, 0.5859375},{0, 0.609375},{0.0234375, 0.609375},{0.046875, 0.609375},{0.0703125, 0.609375},{0.09375, 0.609375},{0.1171875, 0.609375},{0.140625, 0.609375},{0.1640625, 0.609375},{0.1875, 0.609375},{0.2109375, 0.609375},{0.234375, 0.609375},{0.2578125, 0.609375},{0.28125, 0.609375},{0.3046875, 0.609375},{0.5859375, 0.609375},{0.328125, 0.609375},{0.3515625, 0.609375},{0.375, 0.609375},{0.3984375, 0.609375},{0.421875, 0.609375},{0.4453125, 0.609375},{0.46875, 0.609375},{0.4921875, 0.609375},{0.515625, 0.609375},{0.5390625, 0.609375},{0.5625, 0.609375},{0.609375, 0.609375},{0.6328125, 0},{0.6328125, 0.0234375},{0.6328125, 0.046875},{0.6328125, 0.0703125},{0.6328125, 0.09375},{0.6328125, 0.1171875},{0.6328125, 0.140625},{0.6328125, 0.1640625},{0.6328125, 0.1875},{0.6328125, 0.2109375},{0.6328125, 0.234375},{0.6328125, 0.2578125},{0.6328125, 0.28125},{0.6328125, 0.3046875},{0.6328125, 0.328125},{0.6328125, 0.3515625},{0.6328125, 0.375},{0.6328125, 0.3984375},{0.6328125, 0.421875},{0.6328125, 0.4453125},{0.6328125, 0.4921875},{0.6328125, 0.46875},{0.6328125, 0.515625},{0.6328125, 0.5390625},{0.6328125, 0.5625},{0.6328125, 0.5859375},{0.6328125, 0.609375},{0, 0.6328125},{0.1171875, 0.6328125},{0.0234375, 0.6328125},{0.046875, 0.6328125},{0.0703125, 0.6328125},{0.09375, 0.6328125},{0.140625, 0.6328125},{0.1640625, 0.6328125},{0.1875, 0.6328125},{0.2109375, 0.6328125},{0.234375, 0.6328125},{0.2578125, 0.6328125},{0.28125, 0.6328125},{0.3046875, 0.6328125},{0.328125, 0.6328125},{0.3515625, 0.6328125},{0.375, 0.6328125},{0.3984375, 0.6328125},{0.421875, 0.6328125},{0.4453125, 0.6328125},{0.46875, 0.6328125},{0.4921875, 0.6328125},{0.515625, 0.6328125},{0.5625, 0.6328125},{0.5390625, 0.6328125},{0.5859375, 0.6328125},{0.609375, 0.6328125},{0.6328125, 0.6328125},{0.65625, 0},{0.65625, 0.0234375},{0.65625, 0.046875},{0.65625, 0.0703125},{0.65625, 0.09375},{0.65625, 0.1171875},{0.65625, 0.140625},{0.65625, 0.1640625},{0.65625, 0.1875},{0.65625, 0.2109375},{0.65625, 0.234375},{0.65625, 0.2578125},{0.65625, 0.28125},{0.65625, 0.3046875},{0.65625, 0.328125},{0.65625, 0.3515625},{0.65625, 0.375},{0.65625, 0.4453125},{0.65625, 0.3984375},{0.65625, 0.421875},{0.65625, 0.46875},{0.65625, 0.4921875},{0.65625, 0.515625},{0.65625, 0.5390625},{0.65625, 0.5625},{0.65625, 0.5859375},{0.65625, 0.609375},{0.65625, 0.6328125},{0, 0.65625},{0.0234375, 0.65625},{0.046875, 0.65625},{0.0703125, 0.65625},{0.5625, 0},{0.5625, 0.0234375},{0.5625, 0.0703125},{0.5625, 0.046875},{0.5625, 0.09375},{0.5625, 0.1171875},{0.5625, 0.140625},{0.5625, 0.1640625},{0.5625, 0.1875},{0.5625, 0.2109375},{0.5625, 0.234375},{0.5625, 0.2578125},{0.5625, 0.28125},{0.5625, 0.3046875},{0.5625, 0.328125},{0.5625, 0.3515625},{0.5625, 0.375},{0.5625, 0.3984375},{0.5625, 0.421875},{0.5625, 0.4453125},{0.5625, 0.46875},{0.5625, 0.4921875},{0.5625, 0.515625},{0.5625, 0.5390625},{0, 0.5625},{0.0234375, 0.5625},{0.046875, 0.5625},{0.0703125, 0.5625},{0.09375, 0.5625},{0.1171875, 0.5625},{0.140625, 0.5625},{0.1640625, 0.5625},{0.1875, 0.5625},{0.2109375, 0.5625},{0.234375, 0.5625},{0.2578125, 0.5625},{0.28125, 0.5625},{0.3046875, 0.5625},{0.328125, 0.5625},{0.3515625, 0.5625},{0.375, 0.5625},{0.3984375, 0.5625},{0.421875, 0.5625},{0.4453125, 0.5625},{0.46875, 0.5625},{0.4921875, 0.5625},{0.515625, 0.5625},{0.5390625, 0.5625},{0.5625, 0.5625},{0.5859375, 0},{0.5859375, 0.0234375},{0.5859375, 0.046875},{0.5859375, 0.0703125},{0.5859375, 0.09375},{0.5859375, 0.1171875},{0.5859375, 0.140625},{0.5859375, 0.1640625},{0.5859375, 0.1875},{0.5859375, 0.2109375},{0.5859375, 0.234375},{0.5859375, 0.2578125},{0.5859375, 0.28125},{0.5859375, 0.3046875},{0.5859375, 0.328125},{0.5859375, 0.3515625},{0.5859375, 0.375},{0.5859375, 0.3984375},{0.5859375, 0.421875},{0.5859375, 0.4453125},{0.5859375, 0.46875},{0.5859375, 0.4921875},{0.5859375, 0.515625},{0.5859375, 0.5390625},{0.5859375, 0.5625},{0, 0.5859375},{0.0234375, 0.5859375},{0.046875, 0.5859375},{0.0703125, 0.5859375},{0.09375, 0.5859375},{0.1171875, 0.5859375},{0.140625, 0.5859375},{0.1640625, 0.5859375},{0.1875, 0.5859375},{0.2109375, 0.5859375},{0.234375, 0.5859375},{0.2578125, 0.5859375},{0.5859375, 0.65625},{0.6328125, 0.65625},{0.609375, 0.65625},{0.65625, 0.65625},{0.6796875, 0},{0.6796875, 0.0234375},{0.6796875, 0.046875},{0.6796875, 0.0703125},{0.6796875, 0.09375},{0.6796875, 0.1171875},{0.6796875, 0.140625},{0.6796875, 0.1640625},{0.6796875, 0.2109375},{0.6796875, 0.1875},{0.6796875, 0.234375},{0.6796875, 0.2578125},{0.6796875, 0.28125},{0.6796875, 0.3046875},{0.6796875, 0.328125},{0.6796875, 0.3515625},{0.6796875, 0.375},{0.6796875, 0.3984375},{0.6796875, 0.421875},{0.6796875, 0.4453125},{0.6796875, 0.46875},{0.6796875, 0.4921875},{0.6796875, 0.515625},{0.6796875, 0.5390625},{0.6796875, 0.5625},{0.6796875, 0.5859375},{0.6796875, 0.609375},{0.6796875, 0.6328125},{0.6796875, 0.65625},{0, 0.6796875},{0.0234375, 0.6796875},{0.046875, 0.6796875},{0.0703125, 0.6796875},{0.09375, 0.6796875},{0.1171875, 0.6796875},{0.140625, 0.6796875},{0.1640625, 0.6796875},{0.1875, 0.6796875},{0.2109375, 0.6796875},{0.234375, 0.6796875},{0.2578125, 0.6796875},{0.28125, 0.6796875},{0.3046875, 0.6796875},{0.328125, 0.6796875},{0.3515625, 0.6796875},{0.375, 0.6796875},{0.3984375, 0.6796875},{0.09375, 0.65625},{0.1171875, 0.65625},{0.140625, 0.65625},{0.1640625, 0.65625},{0.1875, 0.65625},{0.2109375, 0.65625},{0.234375, 0.65625},{0.2578125, 0.65625},{0.28125, 0.65625},{0.3046875, 0.65625},{0.328125, 0.65625},{0.3515625, 0.65625},{0.375, 0.65625},{0.3984375, 0.65625},{0.421875, 0.65625},{0.4453125, 0.65625},{0.46875, 0.65625},{0.4921875, 0.65625},{0.421875, 0.6796875},{0.4453125, 0.6796875},{0.4921875, 0.6796875},{0.46875, 0.6796875},{0.515625, 0.6796875},{0.5625, 0.6796875},{0.5390625, 0.6796875},{0.609375, 0.6796875},{0.5859375, 0.6796875},{0.6328125, 0.6796875},{0.65625, 0.6796875},{0.6796875, 0.6796875},{0.703125, 0},{0.703125, 0.0234375},{0.703125, 0.046875},{0.703125, 0.0703125},{0.703125, 0.1171875},{0.703125, 0.09375},{0.703125, 0.140625},{0.703125, 0.1640625},{0.703125, 0.1875},{0.703125, 0.2109375},{0.703125, 0.234375},{0.703125, 0.2578125},{0.703125, 0.28125},{0.515625, 0.65625},{0.5390625, 0.65625},{0.5625, 0.65625},
929 | }
930 |
--------------------------------------------------------------------------------