• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mlange-42 / ark-pixel / 13725782068

07 Mar 2025 05:16PM CUT coverage: 28.089% (-19.3%) from 47.374%
13725782068

Pull #8

github

web-flow
Merge c22a6e346 into 2253cb584
Pull Request #8: Add first plot drawers

7 of 677 new or added lines in 11 files covered. (1.03%)

457 of 1627 relevant lines covered (28.09%)

55.34 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/plot/lines.go
1
package plot
2

3
import (
4
        "fmt"
5
        "image/color"
6
        "math"
7

8
        pixel "github.com/gopxl/pixel/v2"
9
        "github.com/gopxl/pixel/v2/backends/opengl"
10
        "github.com/mlange-42/ark-tools/observer"
11
        "github.com/mlange-42/ark/ecs"
12
        "gonum.org/v1/plot"
13
        "gonum.org/v1/plot/plotter"
14
        "gonum.org/v1/plot/vg"
15
        "gonum.org/v1/plot/vg/draw"
16
        "gonum.org/v1/plot/vg/vgimg"
17
)
18

19
// Lines plot drawer.
20
//
21
// Creates a line series per column of the observer.
22
// Replaces the complete data by the table provided by the observer on every update.
23
// Particularly useful for live histograms.
24
type Lines struct {
25
        Observer observer.Table // Observer providing a data series for lines.
26
        X        string         // X column name. Optional. Defaults to row index.
27
        Y        []string       // Y column names. Optional. Defaults to all but X column.
28
        XLim     [2]float64     // X axis limits. Optional, default auto.
29
        YLim     [2]float64     // Y axis limits. Optional, default auto.
30
        Labels   Labels         // Labels for plot and axes. Optional.
31

32
        xIndex   int
33
        yIndices []int
34

35
        headers []string
36
        series  []plotter.XYs
37
        scale   float64
38
}
39

40
// Initialize the drawer.
NEW
41
func (l *Lines) Initialize(w *ecs.World, win *opengl.Window) {
×
NEW
42
        l.Observer.Initialize(w)
×
NEW
43

×
NEW
44
        l.headers = l.Observer.Header()
×
NEW
45

×
NEW
46
        l.scale = calcScaleCorrection()
×
NEW
47

×
NEW
48
        var ok bool
×
NEW
49
        if l.X == "" {
×
NEW
50
                l.xIndex = -1
×
NEW
51
        } else {
×
NEW
52
                l.xIndex, ok = find(l.headers, l.X)
×
NEW
53
                if !ok {
×
NEW
54
                        panic(fmt.Sprintf("x column '%s' not found", l.X))
×
55
                }
56
        }
57

NEW
58
        if len(l.Y) == 0 {
×
NEW
59
                l.yIndices = make([]int, 0, len(l.headers))
×
NEW
60
                for i := 0; i < len(l.headers); i++ {
×
NEW
61
                        if i != l.xIndex {
×
NEW
62
                                l.yIndices = append(l.yIndices, i)
×
NEW
63
                        }
×
64
                }
NEW
65
        } else {
×
NEW
66
                l.yIndices = make([]int, len(l.Y))
×
NEW
67
                for i, y := range l.Y {
×
NEW
68
                        l.yIndices[i], ok = find(l.headers, y)
×
NEW
69
                        if !ok {
×
NEW
70
                                panic(fmt.Sprintf("y column '%s' not found", y))
×
71
                        }
72
                }
73
        }
74

NEW
75
        l.series = make([]plotter.XYs, len(l.yIndices))
×
76
}
77

78
// Update the drawer.
NEW
79
func (l *Lines) Update(w *ecs.World) {
×
NEW
80
        l.Observer.Update(w)
×
NEW
81
}
×
82

83
// UpdateInputs handles input events of the previous frame update.
NEW
84
func (l *Lines) UpdateInputs(w *ecs.World, win *opengl.Window) {}
×
85

86
// Draw the drawer.
NEW
87
func (l *Lines) Draw(w *ecs.World, win *opengl.Window) {
×
NEW
88
        width := win.Canvas().Bounds().W()
×
NEW
89
        height := win.Canvas().Bounds().H()
×
NEW
90

×
NEW
91
        l.updateData(w)
×
NEW
92

×
NEW
93
        c := vgimg.New(vg.Points(width*l.scale)-10, vg.Points(height*l.scale)-10)
×
NEW
94

×
NEW
95
        p := plot.New()
×
NEW
96
        setLabels(p, l.Labels)
×
NEW
97

×
NEW
98
        p.X.Tick.Marker = removeLastTicks{}
×
NEW
99

×
NEW
100
        if l.YLim[0] != 0 || l.YLim[1] != 0 {
×
NEW
101
                p.Y.Min = l.YLim[0]
×
NEW
102
                p.Y.Max = l.YLim[1]
×
NEW
103
        }
×
104

NEW
105
        if l.XLim[0] != 0 || l.XLim[1] != 0 {
×
NEW
106
                p.X.Min = l.XLim[0]
×
NEW
107
                p.X.Max = l.XLim[1]
×
NEW
108
        }
×
109

NEW
110
        p.Legend = plot.NewLegend()
×
NEW
111
        p.Legend.TextStyle.Font.Variant = "Mono"
×
NEW
112

×
NEW
113
        for i := 0; i < len(l.series); i++ {
×
NEW
114
                idx := l.yIndices[i]
×
NEW
115
                lines, err := plotter.NewLine(l.series[i])
×
NEW
116
                if err != nil {
×
NEW
117
                        panic(err)
×
118
                }
NEW
119
                lines.Color = defaultColors[i%len(defaultColors)]
×
NEW
120
                p.Add(lines)
×
NEW
121
                p.Legend.Add(l.headers[idx], lines)
×
122
        }
123

NEW
124
        win.Clear(color.White)
×
NEW
125
        p.Draw(draw.New(c))
×
NEW
126

×
NEW
127
        img := c.Image()
×
NEW
128
        picture := pixel.PictureDataFromImage(img)
×
NEW
129

×
NEW
130
        sprite := pixel.NewSprite(picture, picture.Bounds())
×
NEW
131
        sprite.Draw(win, pixel.IM.Moved(pixel.V(picture.Rect.W()/2.0+5, picture.Rect.H()/2.0+5)))
×
132
}
133

NEW
134
func (l *Lines) updateData(w *ecs.World) {
×
NEW
135
        data := l.Observer.Values(w)
×
NEW
136
        xi := l.xIndex
×
NEW
137
        yis := l.yIndices
×
NEW
138

×
NEW
139
        for i, idx := range yis {
×
NEW
140
                l.series[i] = l.series[i][:0]
×
NEW
141
                for j, row := range data {
×
NEW
142
                        x := float64(j)
×
NEW
143
                        if xi >= 0 {
×
NEW
144
                                x = row[xi]
×
NEW
145
                        }
×
NEW
146
                        if math.IsNaN(row[idx]) {
×
NEW
147
                                continue
×
148
                        }
NEW
149
                        l.series[i] = append(l.series[i], plotter.XY{X: x, Y: row[idx]})
×
150
                }
151
        }
152
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc