-
Notifications
You must be signed in to change notification settings - Fork 0
/
render.go
159 lines (140 loc) · 3.28 KB
/
render.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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
package engine
import (
"github.com/mumax/3/cuda"
"github.com/mumax/3/data"
"github.com/mumax/3/draw"
"image"
"image/jpeg"
"math"
"net/http"
"sync"
)
//var renderer = render{img: new(image.RGBA)}
type render struct {
mutex sync.Mutex
quant Quantity
comp string
layer, scale int
saveCount int // previous max slider value of time
rescaleBuf *data.Slice // GPU
imgBuf *data.Slice // CPU
img_ *image.RGBA
}
const (
maxScale = 32 // maximum zoom-out setting
maxImgSize = 512 // maximum render image size
)
// Render image of quantity.
func (g *guistate) ServeRender(w http.ResponseWriter, r *http.Request) {
g.render.mutex.Lock()
defer g.render.mutex.Unlock()
g.render.render()
jpeg.Encode(w, g.render.img_, &jpeg.Options{Quality: 100})
}
// rescale and download quantity, save in rescaleBuf
func (ren *render) download() {
InjectAndWait(func() {
if ren.quant == nil { // not yet set, default = m
ren.quant = &M
}
quant := ren.quant
size := quant.Mesh().Size()
// don't slice out of bounds
renderLayer := ren.layer
if renderLayer >= size[Z] {
renderLayer = size[Z] - 1
}
if renderLayer < 0 {
renderLayer = 0
}
// scaling sanity check
if ren.scale < 1 {
ren.scale = 1
}
if ren.scale > maxScale {
ren.scale = maxScale
}
// Don't render too large images or we choke
for size[X]/ren.scale > maxImgSize {
ren.scale++
}
for size[Y]/ren.scale > maxImgSize {
ren.scale++
}
for i := range size {
size[i] /= ren.scale
if size[i] == 0 {
size[i] = 1
}
}
size[Z] = 1 // selects one layer
// make sure buffers are there
if ren.imgBuf.Size() != size {
ren.imgBuf = data.NewSlice(3, size) // always 3-comp, may be re-used
}
buf, r := quant.Slice()
if r {
defer cuda.Recycle(buf)
}
if !buf.GPUAccess() {
ren.imgBuf = Download(quant) // fallback (no zoom)
return
}
// make sure buffers are there (in CUDA context)
if ren.rescaleBuf.Size() != size {
ren.rescaleBuf.Free()
ren.rescaleBuf = cuda.NewSlice(1, size)
}
for c := 0; c < quant.NComp(); c++ {
cuda.Resize(ren.rescaleBuf, buf.Comp(c), renderLayer)
data.Copy(ren.imgBuf.Comp(c), ren.rescaleBuf)
}
})
}
var arrowSize = 16
func (ren *render) render() {
ren.download()
// imgBuf always has 3 components, we may need just one...
d := ren.imgBuf
comp := ren.comp
quant := ren.quant
if comp == "" {
normalize(d)
}
if comp != "" && quant.NComp() > 1 { // ... if one has been selected by gui
d = d.Comp(compstr[comp])
}
if quant.NComp() == 1 { // ...or if the original data only had one (!)
d = d.Comp(0)
}
if ren.img_ == nil {
ren.img_ = new(image.RGBA)
}
draw.On(ren.img_, d, "auto", "auto", arrowSize)
}
var compstr = map[string]int{"x": 0, "y": 1, "z": 2}
func normalize(f *data.Slice) {
a := f.Vectors()
maxnorm := 0.
for i := range a[0] {
for j := range a[0][i] {
for k := range a[0][i][j] {
x, y, z := a[0][i][j][k], a[1][i][j][k], a[2][i][j][k]
norm := math.Sqrt(float64(x*x + y*y + z*z))
if norm > maxnorm {
maxnorm = norm
}
}
}
}
factor := float32(1 / maxnorm)
for i := range a[0] {
for j := range a[0][i] {
for k := range a[0][i][j] {
a[0][i][j][k] *= factor
a[1][i][j][k] *= factor
a[2][i][j][k] *= factor
}
}
}
}