-
Notifications
You must be signed in to change notification settings - Fork 0
/
server.go
139 lines (119 loc) · 2.98 KB
/
server.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
package main
import (
"bytes"
"encoding/base64"
"fmt"
"image/color"
"log"
"math/rand"
"net/http"
"go-hep.org/x/hep/hplot"
"golang.org/x/net/websocket"
"gonum.org/v1/plot/plotter"
"gonum.org/v1/plot/vg"
"gonum.org/v1/plot/vg/draw"
"gonum.org/v1/plot/vg/vgimg"
)
type wplot struct {
Plot string `json:"plot"`
}
// Server that opens a websocket connection
// to send points and plots
type server struct {
in plotter.XYs // points inside the circle
out plotter.XYs // points outside the circle
n int // number of samples
data chan [2]float64 // channel of (x, y) points randomly drawn
plots chan wplot // channel of base64-encoded PNG plots
}
func (s *server) dataHandler(ws *websocket.Conn) {
for data := range s.plots {
err := websocket.JSON.Send(ws, data)
if err != nil {
log.Printf("error sending data: %v\n", err)
}
}
}
func plotHandle(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, page)
}
func newServer() *server {
srv := &server{
in: make(plotter.XYs, 0, 1024),
out: make(plotter.XYs, 0, 1024),
data: make(chan [2]float64),
plots: make(chan wplot),
}
go srv.run()
return srv
}
func (s *server) run() {
for v := range s.data {
s.n++
x := v[0]
y := v[1]
d := x*x + y*y
pt := plotter.XY{X: x, Y: y}
switch {
case d < 1:
s.in = append(s.in, pt)
default:
s.out = append(s.out, pt)
}
s.plots <- plot(s.n, s.in, s.out)
}
}
func plot(n int, in, out plotter.XYs) wplot {
radius := vg.Points(0.1)
p := hplot.New()
p.X.Label.Text = "x"
p.X.Min = 0
p.X.Max = 1
p.Y.Label.Text = "y"
p.Y.Min = 0
p.Y.Max = 1
pi := 4 * float64(len(in)) / float64(n)
p.Title.Text = fmt.Sprintf("n = %d\nπ = %v", n, pi)
sin, err := hplot.NewScatter(in)
if err != nil {
log.Fatal(err)
}
sin.Color = color.RGBA{255, 0, 0, 255} // red
sin.Radius = radius
sout, err := hplot.NewScatter(out)
if err != nil {
log.Fatal(err)
}
sout.Color = color.RGBA{0, 0, 255, 255} // blue
sout.Radius = radius
p.Add(sin, sout, hplot.NewGrid())
return wplot{Plot: renderImg(p)}
}
// Approximate PI using Monte Carlo method
// draw a square, inscribe a circle within it,
// uniformly scatter objects of uniform size over the square,
// count the number of objects inside the circle and the
// total number of objects, the ratio of the inside-count
// and the total-sample-count is an estimate of the ratio
// of the two areas, which is π/4. So PI = π / 4 * 4
// area(circle) = πr^2 / 4; area(square) = r^2
// ratio area(circle) / area(square) = π / 4
func (s *server) pi(n int) {
for i := 0; i < n; i++ {
x := rand.Float64()
y := rand.Float64()
s.data <- [2]float64{x, y}
}
// return 4 * float64(inside) / float64(n)
}
func renderImg(p *hplot.Plot) string {
size := 20 * vg.Centimeter
canvas := vgimg.PngCanvas{Canvas: vgimg.New(size, size)}
p.Draw(draw.New(canvas))
out := new(bytes.Buffer)
_, err := canvas.WriteTo(out)
if err != nil {
log.Fatal(err)
}
return base64.StdEncoding.EncodeToString(out.Bytes())
}