File: render.go

package info (click to toggle)
mumax3 3.11.1-1
  • links: PTS, VCS
  • area: contrib
  • in suites: forky, sid
  • size: 10,668 kB
  • sloc: makefile: 194; ansic: 155; sh: 86; javascript: 16
file content (155 lines) | stat: -rw-r--r-- 3,296 bytes parent folder | download | duplicates (3)
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
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"
)

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 := MeshOf(quant).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 := ValueOf(quant)
		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

			}
		}
	}
}