-
Notifications
You must be signed in to change notification settings - Fork 0
/
canvas.go
132 lines (108 loc) · 2.9 KB
/
canvas.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package main
import (
"errors"
"fmt"
"image/png"
"os"
"time"
ebiten "github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/inpututil"
)
// SimulationCompleted is the error returned when the simulation ends by timeout
var SimulationCompleted = errors.New("Simulation completed")
// Canvas handles the canvas visualization
type Canvas struct {
imageName string
numSeeds int
// resolution of the canvas
width int
height int
gameRunning bool
// simulated annealing info
simulatedAnnealing SimulatedAnnealingEngine
simulationDuration time.Duration
simulationStart time.Time
// snapshots logic timers
lastSnapshot time.Time
snapshotsInterval time.Duration
}
// NewCanvas creates a canvas with the simulated annealing ready to start
func NewCanvas(
imageName string,
numSeeds int,
width int,
height int,
simulatedAnnealing SimulatedAnnealingEngine,
simulationDuration time.Duration,
snapshotsInterval time.Duration,
) (*Canvas, error) {
g := &Canvas{
imageName: imageName,
numSeeds: numSeeds,
width: width,
height: height,
gameRunning: true,
simulatedAnnealing: simulatedAnnealing,
simulationDuration: simulationDuration,
snapshotsInterval: snapshotsInterval,
simulationStart: time.Now(),
lastSnapshot: time.Now(),
}
return g, nil
}
// Update computes a new frame
func (g *Canvas) Update() error {
// end the simulation if the timeout has been reached
if time.Since(g.simulationStart) > g.simulationDuration {
return SimulationCompleted
}
// intercept the Space key and start/stop the execution
if inpututil.IsKeyJustPressed(ebiten.KeySpace) {
g.gameRunning = !g.gameRunning
}
if !g.gameRunning {
return nil
}
// take periodic snapshots of the canvas
err := g.savePNG()
if err != nil {
panic(err)
}
// compute the next simulated annealing iteration
return g.simulatedAnnealing.Iterate()
}
// Draw writes the computed frame as a byte sequence
func (g *Canvas) Draw(screen *ebiten.Image) {
screen.WritePixels(g.simulatedAnnealing.ToPixels())
}
// Layout returns the resolution of the canvas
func (g *Canvas) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) {
return g.width, g.height
}
// savePNG saves periodic snapshots of the canvas
func (g *Canvas) savePNG() error {
// skip the saving if the last snapshot is still too recent
if !(time.Since(g.lastSnapshot) > g.snapshotsInterval) {
return nil
}
// get the image data
i := g.simulatedAnnealing.GetSnapshot()
// create a png file for the snapshot
pngFile, err := os.Create(
fmt.Sprintf("./res/%s_%d-seeds_%d.png",
g.imageName,
g.numSeeds,
int(time.Since(g.simulationStart).Seconds()),
))
if err != nil {
return err
}
// save the data into the png file
err = png.Encode(pngFile, i)
if err != nil {
return err
}
// reset the snapshot interval timer
g.lastSnapshot = time.Now()
return nil
}