├── .gitignore ├── LICENSE ├── README.md ├── dentro ├── data │ ├── click.png │ ├── data.go │ ├── dosfont.png │ ├── flower.png │ ├── inject.go │ ├── light.png │ ├── scene.obj │ └── wildtextures-african-grey.png ├── index.html ├── main.go ├── music │ ├── music.mp3 │ └── music.ogg ├── screen │ └── screen.go └── wasm.cmd ├── ep01 ├── data │ ├── GOPHER_MIC_DROP-256.png │ ├── click.png │ ├── data.go │ └── inject.go ├── index.html ├── main.go └── wasm.cmd ├── ep02 ├── data │ ├── ballet-egon-256.png │ ├── click.png │ ├── data.go │ └── inject.go ├── index.html ├── main.go └── wasm.cmd ├── ep03 ├── data │ ├── ashleymcnamara-pride_256.png │ ├── click.png │ ├── data.go │ └── inject.go ├── index.html ├── main.go └── wasm.cmd ├── ep04 ├── data │ ├── click.png │ ├── data.go │ ├── inject.go │ └── wildtextures-african-inspir.png ├── index.html ├── main.go └── wasm.cmd ├── ep05 ├── data │ ├── click.png │ ├── data.go │ └── inject.go ├── index.html ├── main.go └── wasm.cmd ├── ep06 ├── data │ ├── click.png │ ├── data.go │ ├── flower.png │ ├── inject.go │ ├── snowflake1.png │ ├── snowflake2.png │ ├── snowflake3.png │ └── snowflake4.png ├── index.html ├── main.go └── wasm.cmd ├── ep08 ├── index.html ├── main.go └── wasm.cmd ├── hoaxplus ├── 00-intro │ └── intro.go ├── 01-title │ └── title.go ├── cmd │ └── hoax │ │ └── main.go ├── data │ ├── Basehead.obj │ ├── data.go │ ├── data │ │ └── click.png │ └── inject.go └── primitive │ ├── line.go │ ├── objloader.go │ ├── point2d.go │ └── point3d.go ├── index.html ├── loader.js ├── server └── main.go ├── style.css └── wasm_exec.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | *.wasm 8 | 9 | # Test binary, build with `go test -c` 10 | *.test 11 | 12 | # Output of the go coverage tool, specifically when used with LiteIDE 13 | *.out 14 | /.idea 15 | /saved 16 | /.htaccess 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Klaus Post 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Go After Dark 2 | 3 | This is a new web series where we take a look at Go from a different perspective. In this series we drop everything about Go routers, REST, containers, databases and all the boring stuff of everyday Go. 4 | 5 | Instead we will have fun by recreating demo effects from the 90’s. We will mainly focus on graphical effects, but over time we may dive into other topics. 6 | 7 | This is the associated code for each episode. 8 | 9 | To watch the episodes go to https://afterdark.klauspost.com/ for a list of episodes. 10 | 11 | 12 | # Installing 13 | 14 | Install the code in your GOPATH and fetch dependencies using: 15 | 16 | ``` 17 | go get -u github.com/klauspost/gad/... 18 | ``` 19 | 20 | # Running 21 | 22 | Go into the directory you want to run and `go run main.go`. For example 23 | 24 | ``` 25 | cd ep01 26 | go run main.go 27 | ``` 28 | 29 | Data, except music, is embedded into the binaries. To re-generate this run `go generate` in 30 | the folder with the `main.go` file. 31 | 32 | # Web Assembly 33 | 34 | WASM requires Go 1.11. Your `wasm_exec.js` must match the version you use for building. 35 | 36 | In your go installation, you find it in `misc/wasm`. Copy it to the repository root. 37 | 38 | In each folder you will find a `wasm.cmd` that contains the commands needed to build the webassembly. 39 | 40 | The output will be called `fx.wasm` and will be placed in the same folder as `main.go`. 41 | 42 | A local server is needed to execute it. In the repository root execute `go run server/main.go`. 43 | When the server is running, you can see your effect on localhost, for example `http://localhost:8080/ep01`. See the browser console if errors occur. 44 | 45 | 46 | # License 47 | 48 | This code is released under a standard MIT license. See LICENCE file for more information. 49 | -------------------------------------------------------------------------------- /dentro/data/click.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/klauspost/gad/8074eba3799ada5ddcd8a71f794efa03de654fb9/dentro/data/click.png -------------------------------------------------------------------------------- /dentro/data/dosfont.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/klauspost/gad/8074eba3799ada5ddcd8a71f794efa03de654fb9/dentro/data/dosfont.png -------------------------------------------------------------------------------- /dentro/data/flower.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/klauspost/gad/8074eba3799ada5ddcd8a71f794efa03de654fb9/dentro/data/flower.png -------------------------------------------------------------------------------- /dentro/data/inject.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import "github.com/klauspost/gfx" 4 | 5 | func init() { 6 | gfx.AddData(Asset) 7 | } 8 | -------------------------------------------------------------------------------- /dentro/data/light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/klauspost/gad/8074eba3799ada5ddcd8a71f794efa03de654fb9/dentro/data/light.png -------------------------------------------------------------------------------- /dentro/data/wildtextures-african-grey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/klauspost/gad/8074eba3799ada5ddcd8a71f794efa03de654fb9/dentro/data/wildtextures-african-grey.png -------------------------------------------------------------------------------- /dentro/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Go After Dark WASM Intro 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | Canvas not supported 14 |
15 |
16 | 17 | 18 | 19 |
Full Screen
20 |
21 | 22 |
Starting
23 | 28 |

-> Watch Go After Dark Episode 1

29 |

If your browser doesn't support Web Assembly, you can watch a youtube version of this.

30 | 31 | 32 | -------------------------------------------------------------------------------- /dentro/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "fmt" 7 | "image" 8 | _ "image/png" 9 | "math" 10 | "math/bits" 11 | "strings" 12 | 13 | _ "github.com/klauspost/gad/dentro/data" // Load data. 14 | "github.com/klauspost/gad/dentro/screen" 15 | "github.com/klauspost/gfx" 16 | ) 17 | 18 | // Generates binary data. 19 | // To install go-bindata, do: go get -u github.com/jteeuwen/go-bindata/... 20 | // 21 | //go:generate go-bindata -ignore=\.go\z -pkg=data -o ./data/data.go ./data/... 22 | 23 | const ( 24 | renderWidth = 640 25 | renderHeight = 360 26 | ) 27 | 28 | func main() { 29 | fx := newFx("data/scene.obj", "data/flower.png", "data/light.png") 30 | gfx.Run(func() { gfx.RunTimedMusic(fx, "music/music.mp3") }) 31 | //gfx.RunWriteToDisk(fx, 11, "./saved/frame-%05d.png") 32 | } 33 | 34 | type coord struct{ x, y, z float32 } 35 | 36 | func (c *coord) scale(f float32) { 37 | c.x *= f 38 | c.y *= f 39 | c.z *= f 40 | } 41 | 42 | var texts = [][2]string{ 43 | { 44 | `--- >> Welcome to Go After Dark << ---`, 45 | `.oOoOoOoOoOo. .oO0Oo. .oO0OoO0OouoO0Oo.`, 46 | }, 47 | { 48 | `A new web series kicking it oldschool `, 49 | `with some 90s demoscene effects. \o/ `, 50 | }, 51 | { 52 | `We will look at basic realtime effects `, 53 | `and explain everything involved. `, 54 | }, 55 | { 56 | `So you can join the Go After Dark club `, 57 | `and code awesome looking effects. `, 58 | }, 59 | { 60 | `Check out the links below to watch `, 61 | `the first episode. `, 62 | }, 63 | { 64 | `I have material ready for at least 10 `, 65 | `episodes, if there is interest for this.`, 66 | }, 67 | { 68 | `>> Credits << `, 69 | `Coding: sh0dan/VoxPod, aka Klaus Post `, 70 | }, 71 | { 72 | `Music: "Boys can't Fly" by CyberSDF `, 73 | `3D Model: "Rubber Duck" by mStuff `, 74 | }, 75 | { 76 | `Tunnel Texture by Tomasz Grabowiecki `, 77 | `DOS font by int10h.org `, 78 | }, 79 | { 80 | `Pure software rendering in Go, compiled `, 81 | `to Web Assembly by Go 1.11 (beta). `, 82 | }, 83 | { 84 | `There is links to all source code to `, 85 | `build and run this demo. `, 86 | }, 87 | } 88 | 89 | type fx struct { 90 | draw *image.Gray 91 | logW, logH uint 92 | lines [][]byte 93 | dots []coord 94 | mipmaps []*image.Gray 95 | img *image.Gray 96 | tunnelTex *image.Gray 97 | lookup [][]uint32 98 | tunLogW, tunLogH uint 99 | text *screen.Fx 100 | atText int 101 | lastT float64 102 | } 103 | 104 | func newFx(scene, particle, light string) *fx { 105 | var fx fx 106 | 107 | // Create our draw buffer 108 | fx.draw = image.NewGray(image.Rect(0, 0, renderWidth, renderHeight)) 109 | 110 | // Store each line as a slice in a slice. 111 | w, h := fx.draw.Rect.Dx(), fx.draw.Rect.Dy() 112 | fx.lines = make([][]byte, h) 113 | for y := range fx.lines { 114 | fx.lines[y] = fx.draw.Pix[y*fx.draw.Stride : y*fx.draw.Stride+w] 115 | } 116 | fx.dots = make([]coord, 0) 117 | b, err := gfx.Load(scene) 118 | if err != nil { 119 | panic(err) 120 | } 121 | 122 | scanner := bufio.NewScanner(bytes.NewBuffer(b)) 123 | for scanner.Scan() { 124 | t := scanner.Text() 125 | if !strings.HasPrefix(t, "v ") { 126 | continue 127 | } 128 | var c coord 129 | n, err := fmt.Sscanf(t, "v %f %f %f", &c.x, &c.y, &c.z) 130 | if err != nil { 131 | panic(err) 132 | } 133 | if n != 3 { 134 | panic("not 3") 135 | } 136 | c.y *= -1 137 | c.y += 1.8 138 | c.scale(150) 139 | fx.dots = append(fx.dots, c) 140 | } 141 | // Load picture 142 | img, err := gfx.LoadPalPicture(particle) 143 | if err != nil { 144 | panic(err) 145 | } 146 | fx.img = gfx.ToGray(img) 147 | 148 | // Calculate the with in log(2) 149 | fx.logW = uint(bits.Len32(uint32(img.Rect.Dx()))) - 1 150 | fx.logH = uint(bits.Len32(uint32(img.Rect.Dy()))) - 1 151 | 152 | // Ensure that the width and height are actually powers of two 153 | if img.Rect.Dx() != 1<= 0; i-- { 167 | img := image.NewGray(image.Rect(0, 0, prev.Rect.Dx()/2, prev.Rect.Dy()/2)) 168 | fx.mipmaps[i] = img 169 | for y := 0; y < img.Rect.Dy(); y++ { 170 | src0, src1 := prev.Pix[y*2*prev.Stride:], prev.Pix[(y*2+1)*prev.Stride:] 171 | dst := img.Pix[y*img.Stride : (y+1)*img.Stride] 172 | for x := range dst { 173 | // Average 4 pixels. 174 | dst[x] = uint8((uint(src0[x*2]) + uint(src0[x*2+1]) + uint(src1[x*2]) + uint(src0[x*2+1]) + 2) >> 2) 175 | } 176 | } 177 | prev = img 178 | } 179 | fx.img, err = gfx.LoadGreyPicture(light) 180 | if err != nil { 181 | panic(err) 182 | } 183 | // Calculate the with in log(2) 184 | fx.logW = uint(bits.Len32(uint32(fx.img.Rect.Dx()))) - 1 185 | fx.logH = uint(bits.Len32(uint32(fx.img.Rect.Dy()))) - 1 186 | 187 | // Store each line as a slice in a slice. 188 | fx.lookup = make([][]uint32, h) 189 | for y := range fx.lines { 190 | lu := make([]uint32, w) 191 | for x := range lu { 192 | // Number of bits to represent width/height of texture. 193 | const prec = 1 << 16 194 | var angle, distance int 195 | // Number of distance repetitions. 196 | const ratio = 16 197 | distance = int(ratio*prec/ 198 | math.Sqrt(float64(x-w/2)*float64(x-w/2)+float64(y-h/2)*float64(y-h/2))) % prec 199 | angle = (int)(prec*math.Atan2(float64(y-h/2), float64(x-w/2))/math.Pi) % prec 200 | // Store distance as lower 16 bits, angle as upper 201 | lu[x] = uint32(distance) | uint32(angle<<16) 202 | } 203 | fx.lookup[y] = lu 204 | } 205 | fx.tunnelTex, err = gfx.LoadGreyPicture("data/wildtextures-african-grey.png") 206 | if err != nil { 207 | panic(err) 208 | } 209 | // Calculate the with in log(2) 210 | fx.tunLogW = uint(bits.Len32(uint32(fx.tunnelTex.Rect.Dx()))) - 1 211 | fx.tunLogH = uint(bits.Len32(uint32(fx.tunnelTex.Rect.Dy()))) - 1 212 | 213 | fx.text = screen.NewFx("data/dosfont.png", fx.textArea()) 214 | fx.text.DrawText(strings.Join(texts[0][:], "\n"), 0, 0) 215 | return &fx 216 | } 217 | 218 | func (fx *fx) textArea() *image.Gray { 219 | //img := image.NewGray(image.Rect(0,0,renderWidth, 32)) 220 | border := 8 221 | yoff := renderHeight - 32 - border 222 | img := image.Gray{ 223 | Pix: fx.draw.Pix[border+yoff*fx.draw.Stride:], 224 | Stride: fx.draw.Stride, 225 | Rect: image.Rect(0, 0, renderWidth/2, renderHeight-yoff-border), 226 | } 227 | return &img 228 | } 229 | 230 | // Render the effect at time t. 231 | func (fx *fx) Render(t float64) image.Image { 232 | const ( 233 | fpX, fpY = 7, 8 234 | fpXL, fpYL = 8, 8 235 | tunLogW, tunLogH = 9, 8 236 | logW, logH = 8, 8 237 | dmX, dmY = 1 << fpX, 1 << fpY 238 | xMask = 1<>16) & 63 253 | } 254 | for y, line := range fx.lines { 255 | lu := fx.lookup[y] 256 | for x := range line { 257 | v := int(lu[x]) 258 | srcX := ((rng15i() + shiftX + (v & 0xffff)) >> fpX) & xMask 259 | srcY := ((rng15i() + shiftY + (v >> 16)) >> fpY) & yMask 260 | srcXL := ((shiftXL + (v & 0xffff)) >> fpXL) & xMaskL 261 | srcYL := ((shiftYL + (v >> 16)) >> fpYL) & yMaskL 262 | line[x] = uint8((uint32(fx.tunnelTex.Pix[srcX+(srcY<> 8) 264 | } 265 | } 266 | const ( 267 | halfWidth = renderWidth * 0.5 268 | halfHeight = renderHeight * 0.5 269 | ) 270 | 271 | // Offset z on all points over time, effectively moving the "camera" forward. 272 | zoff := 400 + float32(math.Sin(t*math.Pi*4)*300) 273 | // Create rotation matrix 274 | rot := rotateFn(-math.Pi/2+0.075*math.Cos(t*math.Pi*16), 0.3+(1-t)*math.Pi*2, 0) 275 | zMul := float32(t * 0.5 * 200) 276 | if t > 0.5 { 277 | zMul -= float32((t - 0.5) * 200 * 255 * 16) 278 | } 279 | for _, d := range fx.dots { 280 | rot(&d) 281 | z := d.z + zoff 282 | if z <= 0 { 283 | continue 284 | } 285 | invZ := 0.8 / z 286 | x := 200 * d.x * invZ 287 | y := 200 * d.y * invZ 288 | x += halfWidth 289 | y += halfHeight 290 | 291 | zsize := 40 * 255 * 16 * invZ 292 | fx.drawParticleMip(int32(x*256), int32(y*256), int32(zsize)) 293 | } 294 | if fx.lastT > t { 295 | fx.atText++ 296 | fx.text.ClearScreen() 297 | txt := texts[fx.atText%len(texts)][:] 298 | fx.text.DrawText(strings.Join(txt, "\n"), 0, 0) 299 | } 300 | fx.lastT = t 301 | fx.text.Render(math.Min(1, t*2)) 302 | return fx.draw 303 | } 304 | 305 | // rotateFn returns a function that rotates around (0,0,0). 306 | // Supply angles in radians. 307 | func rotateFn(xAn, yAn, zAn float64) func(c *coord) { 308 | var ( 309 | s1 = float32(math.Sin(zAn)) 310 | s2 = float32(math.Sin(xAn)) 311 | s3 = float32(math.Sin(yAn)) 312 | c1 = float32(math.Cos(zAn)) 313 | c2 = float32(math.Cos(xAn)) 314 | c3 = float32(math.Cos(yAn)) 315 | 316 | zero = c1*c3 + s1*s2*s3 317 | one = c2 * s3 318 | two = -c3*s1 + c1*s2*s3 319 | three = c2 * s1 320 | four = -s2 321 | five = c2 * c1 322 | six = -c1*s3 + c3*s1*s2 323 | seven = c2 * c3 324 | eight = s1*s3 + c1*c3*s2 325 | ) 326 | return func(c *coord) { 327 | c.x, c.y, c.z = c.x*zero+c.y*one+c.z*two, c.x*three+c.y*four+c.z*five, c.x*six+c.y*seven+c.z*eight 328 | } 329 | } 330 | 331 | // drawParticle will draw a particle centered at x,y with radius r. 332 | // Input is assumed to be 24.8 333 | func (fx *fx) drawParticleMip(x, y, r int32) { 334 | m := fx.calcMapping(x, y, r, -1) 335 | if m.mipSize != 0 && m.mip == nil { 336 | fx.drawPoint(&m) 337 | return 338 | } 339 | v := m.v0 340 | for y := m.startY; y < m.endY; y++ { 341 | dst := fx.draw.Pix[int(y)*fx.draw.Stride:] 342 | mipLine := m.mip.Pix[(v>>16)*uint32(m.mip.Stride):] 343 | u := m.u0 344 | for x := m.startX; x < m.endX; x++ { 345 | xPos := u >> 16 346 | dst[x] = clamp8uint32(uint32(mipLine[xPos]) + uint32(dst[x])) 347 | u += m.uStep 348 | } 349 | v += m.vStep 350 | } 351 | } 352 | 353 | // drawPoint will draw a single point between the pixel. 354 | func (fx *fx) drawPoint(m *mapping) { 355 | dst := fx.draw.Pix[m.startX+m.startY*int32(fx.draw.Stride):] 356 | dst[0] = clamp8uint32((m.u0*m.v0)>>8 + uint32(dst[0])) 357 | if m.endX < renderWidth { 358 | dst[1] = clamp8uint32((m.u1*m.v0)>>8 + uint32(dst[1])) 359 | } 360 | if m.endY >= renderHeight { 361 | return 362 | } 363 | dst = dst[fx.draw.Stride:] 364 | dst[0] = clamp8uint32((m.u0*m.v1)>>8 + uint32(dst[0])) 365 | if m.endX < renderWidth { 366 | dst[1] = clamp8uint32((m.u1*m.v1)>>8 + uint32(dst[1])) 367 | } 368 | } 369 | 370 | func clamp8uint32(v uint32) uint8 { 371 | if v >= 255 { 372 | return 255 373 | } 374 | return uint8(v) 375 | } 376 | 377 | type mapping struct { 378 | startX, endX, startY, endY int32 379 | u0, u1, v0, v1 uint32 380 | vStep, uStep uint32 381 | // Texture coordinates as 16.16 fixed point. 382 | mipSize uint32 383 | mip *image.Gray 384 | } 385 | 386 | // calcMapping will return a mapping for a sprite with radius r placed at (x,y) 387 | // at the specified mip level. 388 | func (fx *fx) calcMapping(x, y, r, mip int32) mapping { 389 | var m mapping 390 | // Quick discard 391 | if x+r < 0 || x-r > (renderWidth*256) || y+r < 0 || y-r > (renderHeight*256) { 392 | return m 393 | } 394 | // For very small radius we simply draw a point 395 | if r <= 128 { 396 | m.startX, m.endX = (x-r)>>8, (x-r)>>8+1 397 | m.startY, m.endY = (y-r)>>8, (y-r)>>8+1 398 | if m.startX >= renderWidth || m.startX < 0 || m.startY >= renderHeight || m.startY < 0 { 399 | return m 400 | } 401 | m.u1 = uint32(x-r) & 0xff 402 | m.u0 = 256 - m.u1 403 | m.v1 = uint32(y-r) & 0xff 404 | m.v0 = 256 - m.v1 405 | 406 | // Radius times 1x1 pixel value. 407 | rmip := (r * int32(fx.mipmaps[0].Pix[0])) >> 5 408 | m.u0 = (m.u0 * uint32(rmip)) >> 8 409 | m.u1 = (m.u1 * uint32(rmip)) >> 8 410 | m.v0 = (m.v0 * uint32(rmip)) >> 8 411 | m.v1 = (m.v1 * uint32(rmip)) >> 8 412 | m.mipSize = 1 413 | // leave mip nil 414 | fx.drawPoint(&m) 415 | return m 416 | } 417 | mipLevel := mip 418 | if mip < 0 { 419 | mipLevel = int32(bits.Len32(uint32(r>>6))) - 1 420 | if int(mipLevel) >= len(fx.mipmaps) { 421 | mipLevel = int32(len(fx.mipmaps)) - 1 422 | } else if mipLevel < 1 { 423 | mipLevel = 1 424 | } 425 | } 426 | m.mip = fx.mipmaps[mipLevel] 427 | m.mipSize = uint32(1<<16) << uint(mipLevel) 428 | 429 | // output pixels per texture pixels * 256 430 | textureScale := float64(m.mip.Rect.Dx()) / (float64(r) / (128 * 256)) 431 | 432 | // Screen space start, rounded down 433 | m.startX, m.startY = (x-r)>>8, (y-r)>>8 434 | // Screen space, rounded up. 435 | m.endX, m.endY = (x+r+255)>>8, (y+r+255)>>8 436 | 437 | // Calculate rounded difference and convert to texture space. 438 | m.u0, m.v0 = 255-uint32((x-r)-(m.startX<<8)), 255-uint32((y-r)-(m.startY<<8)) 439 | m.u0, m.v0 = uint32(float64(m.u0)*textureScale), uint32(float64(m.v0)*textureScale) 440 | 441 | // Calculate rounded difference and convert to texture space. 442 | m.u1, m.v1 = 255-uint32((m.endX<<8)-(x+r)), 255-uint32((m.endY<<8)-(y+r)) 443 | m.u1, m.v1 = m.mipSize-uint32(float64(m.u1)*textureScale), m.mipSize-uint32(float64(m.v1)*textureScale) 444 | 445 | // Calculate step size per screen space pixel. 446 | m.uStep = uint32(float64(m.u1-m.u0) / float64(m.endX-m.startX)) 447 | m.vStep = uint32(float64(m.v1-m.v0) / float64(m.endY-m.startY)) 448 | 449 | if false && (m.uStep == 0 || m.vStep == 0 || m.u0 >= m.u1 || m.v0 >= m.v1) { 450 | fmt.Printf("r:%d, m:%+v v0:%v, v1: %v scale:%v (%d, %d), x,y (%v, %v)\n", 451 | r, m, 256-uint32((y-r)-(m.startY<<8)), 256-uint32((m.endY<<8)-(y+r)), textureScale/256, m.endX-m.startX, m.mip.Rect.Dx(), 452 | float64(x)/256, float64(y)/256) 453 | // Sanity check 454 | m.mip = nil 455 | return m 456 | } 457 | 458 | // Clip left, top, right, bottom. 459 | if m.startX < 0 { 460 | m.u0 += m.uStep * uint32(-m.startX) 461 | m.startX = 0 462 | } 463 | if m.startY < 0 { 464 | m.v0 += m.vStep * uint32(-m.startY) 465 | m.startY = 0 466 | } 467 | if m.endX > renderWidth { 468 | // Not needed for most 469 | m.v1 -= uint32(m.endX-renderWidth) * m.uStep 470 | m.endX = renderWidth 471 | } 472 | if m.endY > renderHeight { 473 | // Not needed for most 474 | m.v1 -= uint32(m.endY-renderHeight) * m.vStep 475 | m.endY = renderHeight 476 | } 477 | 478 | if false { 479 | // Final sanity to make sure we don't go over due to rounding. 480 | for ; m.u0+m.uStep*uint32(m.endX-m.startX) >= m.mipSize; m.endX-- { 481 | } 482 | for ; m.v0+m.vStep*uint32(m.endY-m.startY) >= m.mipSize; m.endY-- { 483 | } 484 | } 485 | return m 486 | } 487 | 488 | func init() { 489 | var palette [256]uint32 490 | rC, gC, bC := 180, 180, 255 491 | flip := 192 492 | flipScale := int(256.0 * (256.0 / float64(flip))) 493 | flipScale2 := int(256.0 * (256.0 / float64(255-flip))) 494 | for i := range palette[:] { 495 | var r, g, b int 496 | if i < flip { 497 | r = (i*rC*flipScale + 128) >> 16 498 | g = (i*gC*flipScale + 128) >> 16 499 | b = (i*bC*flipScale + 128) >> 16 500 | } else { 501 | r = rC 502 | g = gC 503 | b = bC 504 | r += ((i - flip) * (255 - rC) * flipScale2) >> 16 505 | g += ((i - flip) * (255 - gC) * flipScale2) >> 16 506 | b += ((i - flip) * (255 - bC) * flipScale2) >> 16 507 | } 508 | palette[i] = uint32(r) | (uint32(g) << 8) | (uint32(b) << 16) 509 | } 510 | gfx.InitGreyPalette(palette) 511 | } 512 | -------------------------------------------------------------------------------- /dentro/music/music.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/klauspost/gad/8074eba3799ada5ddcd8a71f794efa03de654fb9/dentro/music/music.mp3 -------------------------------------------------------------------------------- /dentro/music/music.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/klauspost/gad/8074eba3799ada5ddcd8a71f794efa03de654fb9/dentro/music/music.ogg -------------------------------------------------------------------------------- /dentro/screen/screen.go: -------------------------------------------------------------------------------- 1 | package screen 2 | 3 | import ( 4 | "image" 5 | 6 | "github.com/klauspost/gfx" 7 | ) 8 | 9 | type Fx struct { 10 | // main and mipmaps 11 | screen [][]byte 12 | font [128][16]byte 13 | fontLU [256][8]byte 14 | draw *image.Gray 15 | lines [][]byte 16 | cpl int 17 | fgColor, bgColor uint8 18 | } 19 | 20 | func NewFx(file string, dst *image.Gray) *Fx { 21 | var fx Fx 22 | fx.draw = dst 23 | 24 | // Load picture 25 | img, err := gfx.LoadPalPicture(file) 26 | if err != nil { 27 | panic(err) 28 | } 29 | if img.Rect.Dx() != 512 || img.Rect.Dy() != 32 { 30 | panic("image " + file + " size is not 512x32") 31 | } 32 | for i := 0; i < 64; i++ { 33 | for y := 0; y < 16; y++ { 34 | var v, v2 uint8 35 | for x := 0; x < 8; x++ { 36 | v <<= 1 37 | v2 <<= 1 38 | if img.ColorIndexAt(i*8+x, y) != 0 { 39 | v |= 1 40 | } 41 | if img.ColorIndexAt(i*8+x, y+16) != 0 { 42 | v2 |= 1 43 | } 44 | } 45 | fx.font[i][y] = v 46 | fx.font[i+64][y] = v2 47 | } 48 | } 49 | fx.screen = make([][]byte, dst.Rect.Dy()/16) 50 | fx.ClearScreen() 51 | fx.SetColor(224, 0) 52 | // Store each line as a slice in a slice. 53 | w, h := fx.draw.Rect.Dx(), fx.draw.Rect.Dy() 54 | fx.lines = make([][]byte, h) 55 | for y := range fx.lines { 56 | fx.lines[y] = fx.draw.Pix[y*fx.draw.Stride : y*fx.draw.Stride+w] 57 | } 58 | 59 | return &fx 60 | } 61 | 62 | func (fx *Fx) SetDraw(img *image.Gray) { 63 | fx.draw = img 64 | } 65 | 66 | func (fx *Fx) SetColor(fg, bg uint8) { 67 | fx.fgColor, fx.bgColor = fg, bg 68 | for i := 0; i < 256; i++ { 69 | v := uint8(i) 70 | for j := 0; j < 8; j++ { 71 | if v>>7 == 1 { 72 | fx.fontLU[i][j] = fg 73 | } else { 74 | fx.fontLU[i][j] = bg 75 | } 76 | v <<= 1 77 | } 78 | } 79 | } 80 | 81 | func (fx *Fx) DrawText(s string, x, y int) { 82 | line := fx.line(y) 83 | for _, ch := range s { 84 | if y >= len(fx.screen) { 85 | return 86 | } 87 | switch ch { 88 | case '\r': 89 | x = 0 90 | case '\n': 91 | x = 0 92 | y++ 93 | line = fx.line(y) 94 | case '\t': 95 | x = (x + 8) & 0x7 96 | default: 97 | if x >= fx.cpl { 98 | x = 0 99 | y++ 100 | line = fx.line(y) 101 | } 102 | line[x] = uint8(ch & 127) 103 | x++ 104 | } 105 | } 106 | } 107 | 108 | func (fx *Fx) line(y int) []byte { 109 | if y >= len(fx.screen) { 110 | return nil 111 | } 112 | line := fx.screen[y] 113 | if len(line) == 0 { 114 | line = line[:fx.cpl] 115 | for i := range line { 116 | // Set to spaces 117 | line[i] = 32 118 | } 119 | fx.screen[y] = line 120 | } 121 | return line 122 | } 123 | 124 | func (fx *Fx) ClearScreen() { 125 | fx.cpl = fx.draw.Rect.Dx() / 8 126 | for i := range fx.screen { 127 | if fx.screen[i] == nil { 128 | fx.screen[i] = make([]byte, 0, fx.cpl) 129 | } 130 | fx.screen[i] = fx.screen[i][:0] 131 | } 132 | } 133 | 134 | // Render the effect at time t. 135 | func (fx *Fx) Render(t float64) image.Image { 136 | stopAt := int(t * float64(len(fx.screen)*fx.cpl)) 137 | stopAtX := stopAt % fx.cpl 138 | stopAtY := stopAt / fx.cpl 139 | addChar := uint8(t * 1000) 140 | for i, line := range fx.screen { 141 | if len(line) == 0 { 142 | // Fast path for empty lines. 143 | dst := fx.draw.Pix[i*16*fx.draw.Stride : (i+1)*16*fx.draw.Stride] 144 | c := fx.bgColor 145 | for j := range dst { 146 | dst[j] = c 147 | } 148 | continue 149 | } 150 | dst := fx.draw.Pix[i*16*fx.draw.Stride : (i+1)*16*fx.draw.Stride] 151 | add := uint8(0) 152 | if i > stopAtY { 153 | add = addChar 154 | } 155 | for j, c := range line { 156 | dst := dst[j*8:] 157 | if i == stopAtY && j >= stopAtX { 158 | add = addChar 159 | if j-stopAtX < 1 { 160 | add = 0 161 | c = '_' 162 | } 163 | } 164 | if c == 32 { 165 | for y := range fx.font[32&127][:] { 166 | // Copy all pixels 167 | p := y * fx.draw.Stride 168 | v := int32(fx.bgColor) * 2 169 | for i := 0; i < 8; i++ { 170 | org := int32(dst[p+i]) 171 | dst[p+i] = clamp8int32(v - 128 + org) 172 | } 173 | } 174 | continue 175 | } 176 | for y, v := range fx.font[(c+add)&127][:] { 177 | // Copy all pixels 178 | p := y * fx.draw.Stride 179 | for i, v := range fx.fontLU[v][:] { 180 | org := int32(dst[p+i]) 181 | dst[p+i] = clamp8int32(int32(v)*2 - 128 + org) 182 | } 183 | } 184 | } 185 | } 186 | return fx.draw 187 | } 188 | 189 | func clamp8int32(v int32) uint8 { 190 | if v <= 0 { 191 | return 0 192 | } 193 | if v >= 255 { 194 | return 255 195 | } 196 | return uint8(v) 197 | } 198 | -------------------------------------------------------------------------------- /dentro/wasm.cmd: -------------------------------------------------------------------------------- 1 | SET GOOS= 2 | SET GOARCH= 3 | go generate 4 | 5 | SET GOOS=js 6 | SET GOARCH=wasm 7 | go build -o fx.wasm main.go 8 | -------------------------------------------------------------------------------- /ep01/data/GOPHER_MIC_DROP-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/klauspost/gad/8074eba3799ada5ddcd8a71f794efa03de654fb9/ep01/data/GOPHER_MIC_DROP-256.png -------------------------------------------------------------------------------- /ep01/data/click.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/klauspost/gad/8074eba3799ada5ddcd8a71f794efa03de654fb9/ep01/data/click.png -------------------------------------------------------------------------------- /ep01/data/inject.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import "github.com/klauspost/gfx" 4 | 5 | func init() { 6 | gfx.AddData(Asset) 7 | } 8 | -------------------------------------------------------------------------------- /ep01/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Episode 1 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | Canvas not supported 14 |
15 |
16 | 17 | 18 | 19 |
Full Screen
20 |
21 | 22 |
Starting
23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /ep01/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // Generates binary data. 4 | // To install go-bindata, do: go get -u github.com/jteeuwen/go-bindata/... 5 | // 6 | //go:generate go-bindata -ignore=\.go\z -pkg=data -o ./data/data.go ./data/... 7 | 8 | import ( 9 | "image" 10 | _ "image/png" 11 | "math" 12 | 13 | _ "github.com/klauspost/gad/ep01/data" // Load data. 14 | "github.com/klauspost/gfx" 15 | ) 16 | 17 | const ( 18 | renderWidth = 640 19 | renderHeight = 360 20 | ) 21 | 22 | func main() { 23 | fx := newFx("data/GOPHER_MIC_DROP-256.png") 24 | gfx.Run(func() { gfx.RunTimed(fx) }) 25 | } 26 | 27 | type fx struct { 28 | img *image.Paletted 29 | draw *image.Paletted 30 | lines [][]byte 31 | } 32 | 33 | func newFx(imageFile string) *fx { 34 | var fx fx 35 | 36 | // Load picture 37 | img, err := gfx.LoadPalPicture(imageFile) 38 | if err != nil { 39 | panic(err) 40 | } 41 | fx.img = img 42 | 43 | // Create our draw buffer 44 | fx.draw = image.NewPaletted(image.Rect(0, 0, renderWidth, renderHeight), img.Palette) 45 | 46 | // Store each line as a slice in a slice. 47 | fx.lines = make([][]byte, fx.draw.Rect.Dy()) 48 | for y := range fx.lines { 49 | fx.lines[y] = fx.draw.Pix[y*fx.draw.Stride : y*fx.draw.Stride+fx.draw.Rect.Dx()] 50 | } 51 | return &fx 52 | } 53 | 54 | // Render the effect at time t. 55 | func (fx *fx) Render(t float64) image.Image { 56 | offsetX := int(t * 100) 57 | offsetY := offsetX 58 | 59 | // Set screen to index 0 color 60 | for i := range fx.draw.Pix { 61 | fx.draw.Pix[i] = 0 62 | } 63 | // Width and height of the source image 64 | w, h := fx.img.Rect.Dx(), fx.img.Rect.Dy() 65 | 66 | offsetX = int(math.Sin(t*math.Pi) * float64(renderWidth-w)) 67 | offsetY = renderHeight - int(math.Abs(math.Sin(t*math.Pi*8)*100)) - h 68 | 69 | // Range over output height 70 | for y, dst := range fx.lines[offsetY : offsetY+h] { 71 | // Look up source line 72 | src := fx.img.Pix[y*fx.img.Stride : y*fx.img.Stride+w] 73 | // Adjust destination by offset. 74 | dst := dst[offsetX : offsetX+len(src)] 75 | 76 | //for x := range src { 77 | // dst[x] = src[x] 78 | //} 79 | copy(dst, src) 80 | } 81 | return fx.draw 82 | } 83 | -------------------------------------------------------------------------------- /ep01/wasm.cmd: -------------------------------------------------------------------------------- 1 | SET GOOS= 2 | SET GOARCH= 3 | 4 | go generate 5 | 6 | SET GOOS=js 7 | SET GOARCH=wasm 8 | go build -o fx.wasm main.go 9 | -------------------------------------------------------------------------------- /ep02/data/ballet-egon-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/klauspost/gad/8074eba3799ada5ddcd8a71f794efa03de654fb9/ep02/data/ballet-egon-256.png -------------------------------------------------------------------------------- /ep02/data/click.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/klauspost/gad/8074eba3799ada5ddcd8a71f794efa03de654fb9/ep02/data/click.png -------------------------------------------------------------------------------- /ep02/data/data.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-bindata. 2 | // sources: 3 | // data\ballet-egon-256.png 4 | // data\click.png 5 | // DO NOT EDIT! 6 | 7 | package data 8 | 9 | import ( 10 | "bytes" 11 | "compress/gzip" 12 | "fmt" 13 | "io" 14 | "strings" 15 | "os" 16 | "time" 17 | "io/ioutil" 18 | "path" 19 | "path/filepath" 20 | ) 21 | 22 | func bindataRead(data []byte, name string) ([]byte, error) { 23 | gz, err := gzip.NewReader(bytes.NewBuffer(data)) 24 | if err != nil { 25 | return nil, fmt.Errorf("Read %q: %v", name, err) 26 | } 27 | 28 | var buf bytes.Buffer 29 | _, err = io.Copy(&buf, gz) 30 | clErr := gz.Close() 31 | 32 | if err != nil { 33 | return nil, fmt.Errorf("Read %q: %v", name, err) 34 | } 35 | if clErr != nil { 36 | return nil, err 37 | } 38 | 39 | return buf.Bytes(), nil 40 | } 41 | 42 | type asset struct { 43 | bytes []byte 44 | info os.FileInfo 45 | } 46 | 47 | type bindataFileInfo struct { 48 | name string 49 | size int64 50 | mode os.FileMode 51 | modTime time.Time 52 | } 53 | 54 | func (fi bindataFileInfo) Name() string { 55 | return fi.name 56 | } 57 | func (fi bindataFileInfo) Size() int64 { 58 | return fi.size 59 | } 60 | func (fi bindataFileInfo) Mode() os.FileMode { 61 | return fi.mode 62 | } 63 | func (fi bindataFileInfo) ModTime() time.Time { 64 | return fi.modTime 65 | } 66 | func (fi bindataFileInfo) IsDir() bool { 67 | return false 68 | } 69 | func (fi bindataFileInfo) Sys() interface{} { 70 | return nil 71 | } 72 | 73 | var _dataBalletEgon256Png = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x00\xaa\x2e\x55\xd1\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\x00\x01\x00\x00\x00\x01\x00\x08\x03\x00\x00\x00\x6b\xac\x58\x54\x00\x00\x02\xfd\x50\x4c\x54\x45\xff\xff\xff\xef\xea\xf1\xc7\xb4\xca\xb9\x9b\xb4\xb4\x91\xac\xc3\xaf\xc9\xdd\xd2\xe1\xfa\xf9\xfa\xd7\xc8\xdb\x89\x54\x82\x9f\x6e\x9f\xa9\x81\xb5\x91\x64\x8b\xc2\xa6\xbc\xf3\xef\xf3\x95\x62\x90\xbc\x94\xc8\xc1\x99\xcd\xcc\xa0\xda\xda\xab\xe8\xd0\xa1\xde\xb2\x86\xbb\x90\x5b\x89\xfb\xfa\xfc\xe6\xdf\xea\xb8\x8f\xc4\xbf\x97\xcb\xd5\xa8\xe3\xd9\xa9\xe7\xd2\xa2\xe0\xa7\x78\xa5\x83\x4c\x7a\xa5\x71\x94\xeb\xe4\xec\xfc\xfb\xfc\xe2\xd9\xe7\x99\x67\x98\xc4\x9a\xd0\xbd\x8f\xc8\xd0\xc0\xd6\xf8\xf5\xf9\xca\xb8\xd2\xbc\x95\xc8\xcb\x9d\xd8\x7d\x46\x75\xfc\xfc\xfc\xc8\x9c\xd4\xd2\xa6\xe0\x9a\x89\xa4\xbe\x96\xca\xfd\xfd\xfe\x99\x73\x91\xd0\x9f\xb7\xfd\xfd\xfd\xf8\xf7\xf8\xf5\xf1\xf6\xab\x81\xa5\xe9\xd8\xd4\xbd\x95\xc9\xbb\x94\xc7\xf5\xf4\xf4\xa7\x78\xb9\xb6\x87\xc6\xbe\x94\xc9\xc1\x96\xcd\xf3\xf2\xf2\xed\xeb\xea\xf1\xf0\xef\xfb\xfb\xfa\x73\x5d\x57\xdb\xd7\xd5\xaa\xa2\x9c\x86\x7b\x71\x66\x54\x4b\x60\x4f\x45\x6c\x5b\x51\xbd\xb7\xb3\xbd\x95\xc8\x51\x42\x35\x5a\x49\x3e\x84\x6a\x66\xab\x89\x8c\xc5\x9d\xa4\xd7\xab\xb6\xdd\xb0\xbc\xd2\xa7\xb0\xbe\x98\x9e\x95\x77\x76\x76\x63\x5c\xd6\xd2\xcf\x55\x45\x39\x9d\x94\x8e\x5d\x4c\x41\xb8\x93\x98\xe9\xb9\xc6\xfb\xc8\xd8\xfe\xca\xdb\xfd\xc9\xd9\xfa\xc7\xd7\x56\x48\x3c\xbe\x95\xca\xa5\x9b\x94\xf6\xc4\xd3\xf9\xc6\xd5\xf5\xc3\xd2\xf0\xbf\xcd\xf6\xf5\xf5\xbe\x96\xc5\x57\x46\x3b\xbd\x96\xc9\x9e\x7e\x7f\xf2\xc1\xd0\xd4\xa9\xb3\xb8\xb1\xad\x4e\x40\x32\xad\x8e\xbd\xff\xcc\xdd\xf7\xc5\xd4\x86\x72\x6d\x4d\x40\x2f\x81\x67\x72\xe5\xb3\xc8\xf6\xf6\xf1\xfe\xfe\xfe\x9c\x79\xad\x4b\x3c\x2f\xbd\x94\xc8\xe1\xae\xc2\xe5\xe3\xe1\x6f\x4d\x5f\xda\xaa\xbe\xf4\xc2\xd1\xd2\xcc\xc8\x69\x4e\x54\xc8\x98\xb5\xfb\xc9\xd6\xf1\xc0\xce\xb1\x8e\x92\xa5\x84\x86\xcb\xc7\xc4\xea\xe9\xe7\x8d\x83\x7b\x8b\x74\x84\xe3\xb4\xc2\x97\x8d\x85\x4f\x3e\x32\xfd\xcc\xd7\xca\xa1\xaa\x7a\x6b\x63\xc9\xc2\xbd\xfe\xfe\xfd\xdf\xb2\xbe\xd8\xa6\xbe\xd3\xa3\xba\xbe\x8e\xae\xe1\xde\xdc\xb4\xac\xa7\xec\xbc\xc9\xeb\xba\xc9\xe7\xb7\xc5\xec\xb9\xcc\xf4\xc3\xd2\x8f\x64\x80\xce\xa4\xad\xe1\xb2\xc0\xf1\xbf\xcf\xea\xba\xc8\xae\xa7\xa1\xe4\xb5\xc3\xee\xbd\xcc\xb6\x85\xaa\xbe\x95\xc9\xbc\x95\xc9\xe9\xb7\xcb\x45\x36\x28\xdb\xad\xba\xc2\xbd\xb9\xf7\xf6\xf6\xe5\xb6\xc4\xe4\xb1\xc5\xe8\xe6\xe4\xef\xed\xec\xf2\xf1\xf1\xf4\xc1\xd0\xff\xce\xe0\xfe\xfe\xff\xff\xff\xfe\xff\xfe\xfe\xfd\xfe\xff\xe2\xd1\xcc\xc0\xaf\xa8\x57\x51\x55\x60\x60\x63\xf0\xed\xf6\xad\x94\xbc\x93\x73\xa8\x87\x57\x98\x74\x44\x8b\x6a\x39\x83\x62\x31\x7d\x8e\x68\xa1\xbb\xa9\xd1\x7e\x54\x94\x86\x61\x9a\x80\x4f\x97\x95\x65\xaa\x79\x4a\x90\x6f\x40\x87\x9c\x6c\xaf\x81\x51\x8e\x8d\x5c\xa2\xaf\x7f\xc1\xc4\x93\xd4\xbd\x8c\xce\xcf\x9e\xdd\x5a\x29\x78\xa0\x71\xb3\xd7\xa6\xe5\xca\x99\xd9\xc7\x96\xd6\xc1\x90\xd1\xd6\xa5\xe4\xd5\xa4\xe3\xd4\xa3\xe2\xb6\x9e\xc3\xff\xd0\xe2\x90\x5f\x97\x9e\x8b\xce\xbd\x95\xca\x67\x45\x6e\xe2\xb1\xc7\xfc\xfc\xf9\xf5\xf3\xf8\xb4\xa8\xbb\x40\x1f\x54\x37\x14\x4c\x50\x30\x5f\xcc\xac\xdf\x5d\x39\x65\xaf\xa1\xb8\xd7\xb6\xe9\xf0\xd0\xff\xba\x99\xcd\xe8\xc8\xfa\xe3\xc3\xf5\x77\x61\x84\x2b\x08\x3f\xeb\xcb\xfd\xbf\x9f\xd3\xb6\x96\xcc\xd2\xcb\xd4\x89\x74\x96\xe7\xdf\xf4\x74\x51\x84\xe0\xce\xe8\x24\x66\x7c\xfc\x00\x00\x2b\x68\x49\x44\x41\x54\x78\xda\xec\xd4\x85\xb2\xab\x30\x10\x06\xe0\xad\x77\x13\xf4\x6e\xd1\xa5\xa9\xbb\x00\xc7\xdd\xdf\xff\x95\x8e\xf7\xba\x7b\xa6\x7c\xa3\x10\x4f\xfe\x59\x28\x14\x0a\x85\x42\xe1\x4f\x2a\x95\x2b\xd5\x6a\xad\xde\x80\x2d\xd5\x44\x14\x52\x0a\x34\x4c\x0b\xb6\x51\xc3\xb0\x1d\xf7\x0d\x51\xcb\xf3\xd1\x0c\x60\xfb\x84\x18\xc5\x31\x33\x27\xd4\x56\x9d\x6e\x08\xdf\xcb\xaa\x37\xeb\xbd\x3e\xe8\x6f\x30\x74\x9d\x11\x3f\x49\x68\x8c\x38\x81\xef\x31\x9d\x29\xdf\xb7\x87\xbe\x51\xd3\x3f\x33\xbd\x61\x3c\x77\xf9\x05\x2d\xec\xa5\x09\xdf\xb4\x2a\xfb\xb6\xb7\x4e\x89\xda\x1e\x66\xda\x87\x60\x6a\x47\x79\x9e\xf2\x0b\x6a\x0f\x97\xd5\x9d\x6f\x65\x66\x17\x3d\x26\x7a\xee\x3f\xc2\x3d\xd0\x9d\x92\xae\xb3\xe0\x57\x94\xda\xcb\xac\x01\x5f\xb1\x6f\x76\x44\x8b\xde\xf6\x57\x46\x03\x34\x57\x19\xba\xf3\xb7\x11\x60\x6a\xdb\xcb\xee\x01\xbc\x38\x1c\x34\x27\x35\xb3\x5a\xad\x34\xa7\x6f\x6b\xdf\x51\x47\x12\xf1\x06\x8d\x3a\xc7\xa0\xb9\x89\x7f\x92\x9f\x2e\xf8\xfd\x1b\x50\x67\xcf\xd5\xa1\x8a\x68\x0b\x25\xcf\x95\xf0\xfd\xf2\xfe\x4b\xfc\x8d\xce\x05\xf1\x7b\x52\x7f\x02\x9a\xeb\x61\x14\x3b\x23\x7e\x8b\x52\xb1\xec\x5a\x00\x4d\xec\x88\x4b\xa6\x67\xa9\x87\x47\x87\x00\x30\xeb\xe0\x98\xf8\x7d\xc9\xb0\x06\x9a\x0b\x0c\xe9\xce\xe3\x94\xdf\x4a\x48\x76\x76\x0f\x7b\xd8\x91\x6b\xde\xa0\x2b\x54\xfd\x55\xe5\xed\xf9\xdf\x22\x61\x82\xee\x8e\x84\x7b\x72\x92\xf2\x7b\xe8\xa2\xb3\xab\x3a\xca\x6d\x7d\xf0\xab\xaa\x96\x7e\xc4\xfc\xc5\x0b\xe8\x5f\xdf\xdc\xde\x69\x5a\x05\x4f\x4e\x5a\xcc\xc9\x03\xf5\xe5\xf5\xa5\xb6\x11\x46\xf1\xf4\x64\xd0\x83\x34\xb0\xd2\x39\xc8\x44\xc3\x03\x4a\x63\x82\x0e\xae\x44\xa4\x27\x5a\xda\x5a\xb0\x8d\xa2\x6d\x92\x76\x2d\x70\xb7\xf1\xf6\x64\x7b\xbc\xd5\xbd\xf7\xde\xcb\xff\x98\xa1\x83\x9c\x73\xfc\x92\xc6\x7d\xa3\x73\x7f\xdf\xbd\xf3\x49\x4e\x45\x71\x7a\x2a\xb6\x3e\x03\x08\xb8\xc5\x16\xa7\x3e\x8c\x68\x77\x29\x13\xb6\x0a\xbc\x57\x36\xdf\x19\x89\x2a\xb1\x78\x3c\x11\xed\xda\xdd\x76\x00\x3e\xa4\x79\x79\x2f\x01\xe0\xe2\xd5\x64\xc0\x53\xb5\x4b\x61\x9a\x6f\x76\x4b\x98\x60\x3a\x18\xf4\x6f\xb3\x8b\xfe\xf2\x9d\x77\x7e\x8e\x24\xe2\xa9\xee\x9e\xde\xbe\xfe\x74\x26\x99\xcd\xb5\x1b\x80\x2f\x39\xbe\x9c\x00\x57\x58\x8b\xd7\x01\xb0\x36\x00\x44\x1c\x0d\x19\xa7\xdd\xbf\xc4\x7d\xf2\xf1\xc0\xa0\x32\x34\x3c\xa2\x1b\xa6\x69\xea\x7a\x5f\x76\xb4\xb3\xcd\x00\xbc\x47\x13\x00\x52\x09\x80\x3a\x28\xba\x6a\x95\x7f\x03\x80\x4b\x00\x63\xb2\x1d\x80\xa7\x83\xdb\x97\x50\xd2\x96\x9e\x2f\x54\x65\xec\x57\x94\x03\x6d\x76\x06\x38\xa0\xcc\x97\x00\x1c\x3c\xa4\x1d\xac\x01\x10\x11\x47\x76\x63\x6b\x07\x10\x7b\xd8\xef\xb1\x9d\x81\x7e\x90\x3c\x72\x54\xb7\x0a\x0d\xe9\xc7\xb4\xe3\xed\x05\xc0\x2b\xc0\xb2\x57\x97\x98\x2c\x9e\x70\xd5\x26\x0b\x90\x9b\x6f\x5d\x0d\xec\xe0\x20\xe5\xb4\x03\x10\x51\x7a\x7c\xa2\xd0\x2c\xcb\x4c\x4d\xb6\x95\xff\x6f\x80\x9b\x2f\x77\xdb\xe3\x8c\x4f\x85\xea\x6b\x9e\xc6\x94\xed\x14\x0c\x4d\x4d\x4d\x8b\x52\x2b\x01\xd7\x8f\x68\xc6\x2c\xb4\x6a\xbc\x3b\x3e\xdb\x46\xfe\x7f\xfb\x9d\x34\xa0\x7c\x2f\xe0\x91\x62\x73\x3b\x5c\xf5\xc6\x63\x1f\x9c\xef\x68\xf2\x4a\xf8\x14\x07\x4f\x40\xff\xa7\xb6\x04\x2c\xe4\x6d\x00\x8c\xf4\xe8\x62\x1b\x01\xd8\x49\x02\x50\x06\x40\xa4\xcc\x4d\x57\x00\x48\x92\xe4\xc0\xc0\x1d\x6c\x2e\x81\xcb\xbf\xa4\x16\x77\x88\x8c\x1c\x14\x1b\x0c\x3c\x7e\xb0\x6c\x36\xc5\xdf\xb0\x56\x0a\x7a\x3a\xf9\x73\xdb\xd8\xdf\xfd\x07\x10\xa0\x2c\x33\xe5\x6e\xbb\xa6\xe7\x94\x6d\x92\xd3\x1f\x10\x19\x8a\x43\x08\x0b\x90\xe1\x1b\x19\x90\x04\xac\xa9\x27\xdd\x30\xc8\x30\x72\x53\x37\xb8\xd5\xb5\xba\x7f\x73\x7d\x63\xb3\x37\xaf\x1f\x49\xec\xfa\xdf\x1b\xdf\xb5\xb5\x75\xe0\x9d\x53\x5d\xa7\x63\x49\xc4\x92\x25\xf8\x55\xd0\xb9\xcd\xe3\x71\x9d\x99\x1b\x0c\x8b\xbc\x5b\xf0\x01\x4c\x2f\x08\x18\xb1\xa2\x2c\x07\x3a\x24\x22\x42\x85\x42\x40\x51\x35\x24\x8c\x11\x60\x4d\x0b\xc2\x17\xaa\x03\x30\xce\xa6\xba\xf7\x17\x26\xd6\x13\x03\xff\xff\xc9\xff\x12\x3b\x74\x2e\x3a\x78\x5e\x8d\x4d\x23\x8a\x18\x92\xf7\xca\xa2\xdf\xdf\xb1\xa3\xa8\x85\x58\x1f\xc2\x98\x5e\x9d\xb8\x30\x03\x30\x10\x18\xc8\x43\x28\x8a\x90\x77\x3b\x10\xf6\xf6\xa8\xea\x12\xe6\x58\x18\x94\xc3\x62\xb5\x1c\x01\x87\x37\x6f\x55\xe7\x7f\x36\x75\x71\xdc\x22\x0d\x50\x3b\xff\xe3\x58\xe7\x8e\x9f\x8b\x5e\xba\x34\x79\x3a\x72\x79\xf1\xeb\xbf\x7e\xcb\x95\xcb\x87\x34\x55\x8d\x65\xd2\x57\x97\x11\x17\xe4\x65\x22\x86\x09\xff\x48\x69\x2a\x71\x0f\x42\xd7\x26\xd6\xac\xc2\xda\x75\x84\x11\x70\x08\x14\x45\x09\x02\x0d\xc8\xf3\x0b\x47\x63\x73\xa9\x1b\x00\x3b\xe6\xa1\xcc\xc0\x12\x01\x49\xe4\x39\x6f\x75\x0d\x5a\x2b\x99\x4b\x3d\xc3\xa6\x35\x91\x8d\xfe\xa7\xf6\x17\x07\x62\xc5\x64\xe2\x52\x2a\x95\x52\xe2\x9a\x9a\xdd\x77\xf3\xcd\xb7\xfc\x74\x2b\xab\x6a\x6a\x2a\x7d\xf5\xf6\x9d\xbb\xc3\x5e\xec\x80\x65\x02\x32\x84\x42\x52\x3d\xe9\x5d\xbd\xb7\x66\x96\x67\xba\x76\x8d\xc3\x44\x08\x21\x5c\x12\x77\x6d\xcd\x98\x9c\x8b\xdd\xbf\x17\xc2\x80\x84\x80\x19\x0b\xf8\x03\x41\xc8\xe2\x07\xfd\x2b\x7a\xe9\x1c\x1c\xe9\x4e\x17\x7a\x36\x37\x8c\x87\xea\xd6\x5b\xfe\x62\x2e\xda\x35\xfb\x8f\x45\x7b\x40\x8d\x67\xd2\x8f\x1e\xe7\x0d\xc3\xbc\xff\xe4\xd8\x46\x6a\xb0\x18\xcd\xd9\xec\x3f\x4d\x90\xe1\x6b\xda\xc5\xc7\x77\x9f\x3d\x7b\x76\xf7\x1a\xc0\x3e\x37\xe4\x79\xc8\xb3\x34\x4e\x4e\x65\x4d\xa3\xbe\xd6\xd6\x9e\xbf\xe0\x50\x49\x00\x70\xbe\x1b\xcf\xd7\x48\xba\xcf\xab\x0f\xc7\xd7\x76\x92\x10\xbc\x24\x08\x5e\xf1\x90\x05\x58\x9b\x4b\x6c\xf6\x8d\x18\x13\xcf\x37\x0a\x13\x86\x75\x76\x24\x93\x78\xdb\xfd\xe0\xb9\x43\xda\xad\x7f\xc8\x7f\x67\x4c\xcb\x9c\xd5\xf5\x4a\x2b\xad\x89\x11\x7d\xe4\x6c\x77\x6c\x2e\xda\xb4\x96\x7f\xed\x4a\xa8\xc5\xc4\xd0\xc3\xd1\xe2\xd0\x9d\x67\x25\x0d\x13\x3b\xc0\xc1\xb2\x14\x4d\x4a\x9e\x99\x8b\x3f\x69\xba\xae\xcb\xaf\x3d\x5e\xd8\xb9\xf3\xda\xc2\xcc\xeb\xe7\x13\x6b\xe4\x79\xf3\x6a\x72\x6e\x43\x2f\x5c\x58\xe0\x30\x47\x85\x83\x61\xd6\x81\xd1\xea\x9f\xc4\x9c\xcf\x4f\x22\x5b\x9b\xc7\xcf\x8e\xe5\x2c\x7b\x39\x37\xee\xd9\x98\x91\x8c\x89\x1b\xb4\x11\xaa\x8a\x1c\x00\x55\xca\x03\x40\x15\x86\x42\x0d\x51\xa9\x48\x2d\xc6\x9c\xc4\x8c\x24\x2d\x2a\x23\x15\x8b\xb7\x33\xaf\x02\xe9\xd8\x23\x70\x17\x47\xe1\x55\xe7\xbe\x33\xb7\xfb\xbd\x7f\xda\x54\x51\xa8\x05\xd4\x01\xbd\x23\xfa\xd9\x74\xdb\xad\x15\x9e\x6f\x3d\xe7\xf9\x75\xce\xf1\x2a\x12\x20\x28\x18\x3e\x14\xf3\x92\x11\x08\x76\xb8\x32\x18\x8d\x9b\x43\x81\x09\x15\x0a\x85\x75\x26\x2c\xf5\x17\x66\x29\xa1\x15\x8a\xad\xfb\x9f\xbc\x4f\xd3\xcd\x2f\x36\x37\x6f\x58\x1c\xaa\x36\x4d\x05\xfe\xfd\x17\xd3\xc7\xff\xf9\xd7\xd4\x95\x2c\x87\xc5\x0d\x0b\xf9\xcc\x97\x2f\x5f\x32\x99\x4c\x6f\x99\xef\x70\x38\x22\xe8\x0f\xad\xfe\xab\x11\x1f\xf4\xb8\xf0\x4f\xfa\xba\x10\xeb\x45\x0f\xc6\x9e\x62\x55\xc8\x74\x35\xba\x05\xa3\xf1\x67\x51\x09\x4c\x84\x8b\x18\x77\xb3\xb9\x31\x48\x7e\x6f\x99\x57\xa3\xfb\x40\xa7\x1d\xcd\xca\xb1\xe3\xef\xd5\x4e\xe7\x2b\x83\x23\xb5\xa6\x49\x3d\xf1\x9f\xbf\xfe\xf2\xcb\x2f\x7f\xfd\xdb\x97\xd4\x35\x63\x18\x48\x27\xa9\x90\x60\xd2\x70\x8d\xd4\xdf\xfe\xaa\xff\xc8\xaf\xff\xb2\xf3\xc5\xf8\x4a\xd8\x38\x0f\xca\x84\x3d\x4e\x08\x79\x21\x0e\x2f\xc0\x68\x94\x2c\x3a\x01\x93\xc0\xa9\xc5\x76\x6b\x9b\x92\xcd\xc7\x3e\x8c\xe0\x92\x13\x80\x45\x1e\xca\xc1\xdd\x56\xdd\x78\xed\x3c\xe6\x13\xcd\x47\xea\xad\x7c\x26\xf5\x45\x7f\x7f\x46\x94\xdb\xcc\x6f\x50\x11\xb6\x89\x67\x43\x32\x57\xc7\x17\xfd\x47\xbe\x48\x8f\x15\x60\x72\x49\x81\x84\x0d\x55\xf7\x82\xfc\xcc\x98\x28\x1d\x43\xda\x64\xb6\x93\x72\x30\xd4\xaa\x1f\x6e\xd8\x90\x49\x6e\xab\x65\x70\x19\x90\x03\xa1\x84\x69\x76\x35\x82\xd9\x9b\x7a\xd3\xe4\xec\xfa\x9b\x24\x99\x06\xae\xca\xf0\x5c\xdc\xa0\xb2\x57\x24\xdc\x93\x40\x92\x55\x6b\x29\x29\x2e\x29\x32\x61\xb6\xd9\x71\x6f\xd7\x8b\x26\xd5\x2e\x6b\xcc\x59\xe7\x5a\xda\xb0\x23\x2f\x1c\xcb\x7c\x56\xf5\x5c\xb5\x3a\xcd\x2e\xd5\xa2\x0c\x57\xaf\x7b\xf6\xd7\x9e\xbf\xaf\xca\xe0\xc8\x08\x01\x84\x38\x66\x5a\x34\x0f\x49\x8a\x2b\x41\x8c\x99\x02\x18\xc9\x67\x1e\x71\x6e\x30\x09\xee\x50\xa4\x75\x56\x95\x36\x6c\xc9\x8b\x11\x0c\x83\xdf\x6b\xcd\x1e\x89\x95\x00\x8e\x54\x07\xed\x37\x7c\x1c\xc7\x9a\x99\xb1\x02\x50\x10\x33\x61\x0e\xc3\xd2\xe9\xc8\x40\x8d\x90\x17\x4c\x84\x13\x39\x5e\x3d\x6b\x49\x1b\xf6\x64\x0e\x3d\x70\xa9\xd6\x7c\xa4\x53\xf7\x60\xee\xcc\x70\x07\xab\x66\x66\xa2\x0b\xed\x8d\x12\x80\x3b\x1a\x11\x23\x24\xa1\x75\x9c\x95\x59\x3f\xa0\x53\x42\xcc\x84\x7a\x45\xbf\xee\xd3\x4f\x02\x48\xa9\x64\xaa\x5f\x0b\xe1\xbc\xf7\xca\x4d\xaa\xc7\x18\xae\xd6\x8c\xf7\x2f\xf5\xbf\xc4\xc8\xa8\x77\xbc\x17\x22\x7c\xdf\xd2\xb7\xc9\x39\xbb\x1e\x0c\x4b\x0b\xd4\x4c\x15\xc8\x46\xc1\x04\x05\xe8\x99\x23\x1d\xad\x9c\x2f\x1d\x09\xd6\x92\x40\xca\x7b\xd8\xaf\xf5\xa6\x75\x0d\x04\x6b\x7a\xfc\xdb\xe8\x27\xb5\xdc\xad\x75\x28\x88\xdb\x24\x28\x6e\x8c\x46\x3c\xdc\x86\x32\x47\xeb\x87\xa2\x88\xb9\x03\x93\xe1\xc4\x28\x6d\xea\xa6\xb1\x1b\xa1\x88\xc2\x31\x91\x1b\x21\xd3\xb7\xbc\xc3\x89\xe6\x13\xd7\x41\x1c\x58\x49\xd4\xbf\x0d\x79\x79\x84\xd0\xbd\x3c\x15\x24\xdb\x14\x75\xac\x11\x37\xcc\xaa\x31\xfb\x72\x68\x9e\x41\x69\x30\x21\xee\x90\xa2\xbb\xb8\x59\x07\x1c\x1e\x25\xc5\x56\xdc\x13\x8b\xd4\x9f\x07\xb6\xc2\x2a\x29\xd6\x9a\x4f\xd4\x56\x21\x8e\xb4\x76\xa4\xa1\x60\xd1\x61\x71\x7c\x8f\x62\xda\x26\x87\x57\x85\x8d\xb1\x08\x1d\x1e\xa3\x86\x7d\x0e\xe4\xee\xc1\xa4\xd0\xd8\xe5\xfa\x59\xbd\xe7\xf0\xc6\x8b\x10\x6f\xb2\x81\xb8\xf8\xf8\x36\x93\x57\xcc\xb9\xc5\x03\x3a\x1d\x0f\x0e\x2c\x27\x6d\x3e\x7d\x91\x30\xd5\x8c\xbd\x03\x2c\xa1\xec\x72\x6a\x63\x3c\xc9\x6f\x11\x19\x45\x6d\x1d\xe0\x12\x4c\x8c\x9c\x1a\x6a\xe9\x6f\x56\x7a\x8e\x47\xdb\xfc\x39\x1f\x91\x7a\x0a\x48\x2d\x3d\xea\x5b\xa8\xc6\x65\xdb\xba\x57\x92\x78\x72\x6c\xef\x02\x42\xc8\x2c\x04\xc7\x93\x11\x43\x10\x0e\x3b\x7b\x34\xa0\xb9\xec\xd7\x6f\xe3\x02\xfc\xbf\x79\x60\xd8\xe5\x44\xf3\x29\x11\xa4\xfe\x7b\x5b\xd9\xd9\x3b\x0c\x2a\x92\x64\xda\xdf\xb4\xda\x6f\x64\x42\x1e\x67\x77\x6d\x5c\x40\x5c\x42\x81\xaf\x76\xef\x59\xca\xeb\xd2\x0c\x4a\x46\x4d\x88\xf1\x61\x05\xee\x58\x5a\x17\x50\x7a\x93\xfe\xe8\x12\x2a\x55\xdd\xc8\x5a\xf7\x25\x65\xbe\xf3\xdb\xa9\xcc\xb7\xbc\x14\x3a\xd3\x5d\x40\xfa\x56\x6b\x0e\x72\xbd\xbb\x8e\x83\x19\xc9\xb6\xe0\x0d\x0a\x06\xa2\x98\xca\x4b\x16\xc7\xde\x85\x94\x15\x60\x83\xb4\xb7\x8a\x06\xfd\xbd\x4c\x33\x73\x86\x7f\x93\xea\x68\xbf\x84\x8f\xf5\x5a\xf7\x2c\x71\x24\x49\xd2\x37\x25\xde\x0d\x80\x92\xd8\x4d\x8b\xf5\xb3\xe6\x00\x67\xb5\xbd\x6d\x2c\xc7\x85\xc1\x40\x97\x14\x84\x94\x82\x43\xe7\xab\xab\xab\xe1\xab\x9b\xc4\xa1\x28\x24\xf3\x4f\x09\x42\x11\x37\x5e\xcc\xde\x2a\x84\xb7\xfd\x45\x20\x7b\x40\x09\xe0\x81\xb7\x69\x10\xef\x3d\x30\xd4\xd2\x57\x41\xf3\x7a\x47\x3a\x4c\x3c\x79\xb7\xf4\xad\xaa\xff\xdb\x20\x9d\x9d\x4c\x8b\xc3\x6c\x27\xd5\x57\x31\x0b\x3b\xbb\x45\xc5\xc3\x42\x8c\x09\x21\x58\x45\x31\x8f\x12\x5a\x69\x09\xa2\x64\x94\x08\x59\xb4\x64\x3c\xf4\xc5\x3e\x50\x84\x01\x4b\x67\xe0\xd0\xa8\x5d\x90\x1f\xb1\x6f\x53\x1e\x1e\x70\xb2\xb2\x64\x4a\xf0\x5f\x79\x49\xea\x05\xb5\xcd\xe1\xd7\x6f\x96\xc0\xc2\x15\xc2\x4a\x52\x7a\x8a\x5c\xc2\xe1\x55\x84\x83\x84\x60\x18\xe3\x78\x4d\xd3\x78\x2e\x20\x13\x42\x20\x13\x39\x3f\x14\xf2\x42\x84\x52\x04\xd0\x90\x7e\x2b\x42\xcf\x5a\xa1\x52\x29\x2c\xce\xef\x03\xd0\xc8\xfa\xe8\xf5\xb1\xef\xad\x46\xa2\x8a\x1c\x8b\x5c\x5d\xd7\x3a\x67\xcd\x7a\x6d\x73\xe7\xf0\xf0\x70\x67\x33\x71\xd6\xb4\xa1\x73\x68\x38\xf5\x31\x96\xe3\x7b\x8f\xe6\xd7\x43\x1e\x95\x60\x56\x49\xfb\xd7\x2e\xe6\xdd\x73\x6d\x97\x63\xeb\x6e\xcd\x1f\xd5\x62\x84\xa8\x9e\xe2\xd1\x8a\x4c\x4b\x8f\xb4\xee\x70\xf9\x38\x8b\x4d\x02\x7c\x34\xc7\x72\xf3\x34\xcf\x7d\xc3\xec\xe8\xcc\x71\x38\x1b\x8c\xeb\x53\x9f\xb3\x33\x23\xd2\xd7\x3b\x36\xde\xff\xd4\x03\x49\x79\x05\x07\x96\xba\x51\x42\x48\x1c\xb3\x84\x30\xe5\xdb\x3b\xd7\xcc\x94\xab\x3d\xd7\x76\xec\x83\x1e\xa7\xb7\x25\xe3\xbf\x18\xb4\xf2\x0a\x07\x90\x84\x65\x63\x44\xe2\x8b\x36\x72\xb9\x46\x54\x61\x20\x56\xb5\xc5\xbf\x50\x57\xc0\x29\x78\x33\xda\x7e\x05\xca\x4c\xa8\x69\xe9\x7c\x28\x0e\xa0\x93\xd9\xf4\x10\xae\x9a\xda\x48\x4a\x45\x96\xa8\x5a\xee\xd4\x39\xb5\xe5\x76\x3f\xcc\xb9\x66\x06\x2a\x98\x4b\x0f\x26\xb1\x70\xf2\xc5\x1e\x90\x4f\x16\x11\xd1\xfc\x77\x0e\x60\xb2\xb5\x98\xf6\xc8\x58\x2b\xd8\x97\x70\xa8\xfc\xc6\xc3\xc1\x68\x0c\x73\xe1\x6a\xa2\x49\xe5\xec\x71\x72\x92\xac\x73\x24\x98\xdf\xbb\xe1\x09\xd6\x2a\x6d\xa7\x61\xfd\xc3\x96\xdd\x59\x2f\xc7\x2d\x4f\xb0\x72\x2d\xbe\xd0\xfe\xd4\x36\x61\xfc\x2e\xa7\xf5\x51\x9f\x0b\x25\x84\x7d\x36\x05\x4f\x05\xbd\xfd\xa9\x9a\xfb\xf4\x3a\x54\x56\xaa\xd7\xf5\x26\x85\xa3\xc7\xd0\x27\x36\x39\xb2\x1d\x5f\x27\x9c\xdf\xf5\xd9\xad\x63\x98\x6f\x8f\xab\x11\x20\xcc\xca\xde\x8b\xfc\x5f\xdc\x26\xda\xc1\xb4\xfb\xc1\x31\x30\xb6\x57\xd4\x6c\x63\x70\x66\xb8\xc5\xa3\xf2\x24\x76\x88\x7c\x38\xab\x84\xbf\xd6\xed\x25\xa8\x5b\x62\x55\x35\xa8\x62\x5c\xbe\x9b\xe9\x9a\x3f\xe7\x18\x19\x62\x09\x32\xaa\x8b\xb1\x08\xab\x84\xbf\x77\xb8\x1f\xda\x60\x90\x5b\x06\xfb\xe6\x87\x3a\xe4\x53\x30\x01\xf6\x6f\x79\x0c\x19\xae\x78\x4d\x0d\x81\x8f\x7b\xda\x41\xbc\x9e\x9b\x6a\x77\xed\x77\xed\x8f\x0e\xb1\x5e\x8c\x43\xe2\x58\x05\x32\x35\x36\x76\xe1\xd0\x9f\x37\x63\xd3\x0f\xf9\x70\xff\xb0\x20\x87\x50\x05\x4c\x06\xe7\xa2\x07\xe2\x78\x8d\xbe\x02\x4c\x07\xe0\x09\x53\xf8\x7b\xd7\x7c\xb7\x13\x8c\xe3\x16\x91\xd0\x58\x1f\x10\x8e\xc9\xa5\xfe\xc4\x07\x87\xed\x8b\x49\xab\x01\x4b\xd9\xd7\xc8\x22\x2f\x98\x14\x0e\x0e\x06\x76\xed\x42\xe1\xf3\x1c\x24\xd5\xe2\x09\x77\xe1\xec\xda\xdf\xde\x07\xe3\x29\x04\x48\x51\x18\x13\x01\x37\x59\xce\x3d\xe7\x76\xcf\x51\x9e\xe7\x87\xeb\x8f\x0a\x38\xa2\x59\x94\x06\x13\x63\x9e\x85\xc6\x18\x6c\x00\xeb\x20\x30\x9f\x0f\xea\xf6\x9b\xd1\xcf\x05\x5e\xc4\xda\xba\x1a\x1e\xad\x80\x78\x4e\x1a\xd3\xa3\x1e\x78\x0b\x63\x85\xde\x4e\x1d\xca\x5e\x5a\x5c\x16\xbc\x31\x17\x01\xc8\x35\x3b\x36\x02\x6c\x4a\xcf\x5b\x3d\xb1\xc5\x29\x8a\xfd\x14\x4e\x60\x60\x79\x64\x36\x14\xb6\xe1\x85\x4b\x7f\x24\xdd\x1e\xbf\xcc\x5e\x00\xf0\x10\x0d\x20\xcf\x9a\x65\x6b\x5b\x2b\xbf\x71\x3a\x5c\xcc\xca\x9e\x4e\xa7\x39\xcc\xce\xe3\x47\x0d\xcb\xea\xed\x34\xc5\x7e\x2a\x39\xe2\xd9\xc9\x6f\xd0\x49\xf1\xfc\x96\x91\x4f\x01\x1d\x2f\xd6\x1e\x6e\x19\x14\x4b\xb7\xad\xbe\x85\xd8\x93\x37\x16\x00\xc9\x7c\xbd\x43\x0f\x01\x99\x1a\x43\xa2\xa6\xfd\x5b\xe0\x15\x94\x47\xce\x45\xa4\x23\xa6\xe4\x1c\x27\x69\x09\x33\x68\xbd\x7c\x07\xac\xa4\x91\x6f\xe1\xcd\x3d\x40\x17\x80\x5e\x06\xea\x0b\x80\x5f\xd0\x5f\x96\x7d\xc2\xa2\xe3\xe6\xd4\xdd\x24\x3d\x06\x26\x02\x97\x4e\x33\x07\xd0\xb9\xe7\x54\xdf\xdd\x50\x4b\xe4\x7f\xf3\x18\x60\x2c\x01\x6a\x15\x90\xbc\x41\x72\x65\xca\x6d\xf0\xf9\xb5\x23\x78\x2d\x23\x51\xab\x80\x26\xf2\x1b\x4f\x75\x8e\xce\xa7\xea\xe0\x1e\x71\xfa\xed\x37\x0d\xef\x62\x46\x10\xa4\xc6\x40\x51\x21\x3e\x87\x99\x00\xc1\x2b\x29\x11\x7a\x26\xc8\x9c\x41\xbf\x73\x84\x53\x39\xef\x4f\xdd\xdd\x45\xd0\xe8\xcf\xaf\x13\xd8\x35\xfe\x37\x06\x32\x67\x1d\x5a\x19\xa4\x4f\x78\x60\xc1\x41\x59\x00\x63\x38\x40\x9e\x3c\x75\x1b\xb2\x93\xbd\x35\x3c\xc0\xbe\x0a\x70\x37\x34\x36\xc0\x6a\x15\xdd\x3d\xfb\x2a\xe0\x0b\x06\xf9\xfe\x02\xde\x98\xcf\x1e\xc8\xde\xd4\x69\x49\x40\x8f\x00\x8a\x8b\xe6\x00\xe3\xe3\x20\xf5\x1c\x41\xbe\x16\xf3\x53\x05\x58\xf4\x10\x15\x62\x3e\x57\x98\x07\x51\x1c\xb5\x9e\x6e\xca\xf2\x13\xd8\x34\x55\x90\xbe\xfd\x45\x99\x05\xe4\x37\x19\x92\x73\xba\x0d\xfa\x17\xa3\x6b\x7e\x61\xbc\x47\x5c\xc0\x20\x2d\x0c\x4a\x3b\x8c\xdf\x49\x11\xe0\x80\xc5\x08\xa9\x9a\xcb\x1c\x92\x3f\xef\x13\xfb\x63\x59\x66\x12\xc7\x2b\xcb\x08\x86\xaf\x29\xad\xa0\x78\x25\xb3\x07\xdd\x16\x68\xce\xea\xa1\x39\x1f\xc7\x30\xdc\xe5\xd8\xb2\xc0\x07\xbf\xa6\x68\x75\x80\x27\x47\xf3\x00\x1f\x46\x08\xc1\x42\xaf\x0d\x7c\x8c\x02\x77\x25\x84\xf8\x0b\x30\x01\x1a\x48\x1e\x6e\x86\xce\x12\x8f\x2b\xc0\xe7\x18\xac\x81\x2a\x1c\xc6\xb2\x0a\x1b\x0b\x63\xdb\x82\x0a\xbd\x25\x10\x95\x34\xc5\x03\x16\xa1\x61\x7f\x6f\xfe\xe5\x58\x5c\xe7\x1d\x86\xe6\x5e\x16\x65\xcb\x6e\x30\x09\x4e\x90\x7c\x5c\xb5\x17\xc0\xd8\xe6\xf1\x9a\x4d\xd0\xe7\x67\xc1\x54\x15\x21\xa4\xbe\x24\x1a\xbb\x59\x3e\x43\x6d\x06\x7d\x94\x2c\xe0\xc5\x48\x27\x6b\x3a\x7b\xe1\xc2\x87\x0b\xe0\xbe\xc1\x20\xc4\x9d\x80\xc9\x70\x10\x90\x15\x1b\x01\xa4\x6e\xb9\xc2\xe2\x93\x4f\xfd\x39\x20\x87\xa1\x61\xbf\xe6\x7c\x51\x26\x84\xb4\xb3\x34\xe2\xaa\xe2\xb2\xaf\x2d\xca\x5d\x01\x70\xd9\x5c\x0e\x85\x5b\xec\x2b\xb3\xba\xf9\x39\x17\x98\x10\x2e\xce\xa8\x84\x6c\x0b\xc1\xe4\x12\x64\x2f\xda\x6e\x83\xfd\xe7\xb2\x01\xe9\xe0\xe8\x0b\x3b\x82\xb0\x48\xc9\x83\xcb\x9e\xd3\x2d\xbd\x12\xa4\x09\x80\xd4\xf2\x5d\xfb\xa0\x84\xd7\xe6\x03\x10\xb1\xa5\x8a\x0b\x4c\x8e\x92\x91\x07\x6d\x05\x10\xc3\x98\xb9\x9f\xeb\x13\x20\x8a\xd1\x2b\x04\x58\xc4\xdb\x02\xad\x19\xe0\x17\x5d\xb6\xbd\x40\x0e\xa3\x2e\x38\xc6\xad\xe3\xc0\x29\xe0\xd1\x49\x1b\x4c\x94\x06\x42\xe1\x6b\x5b\x01\x84\x22\xe6\x1f\xba\x02\xcc\xed\x3f\xb9\x8b\xf9\x7e\x14\x60\x32\xb7\x06\x46\xb0\xc5\x06\x53\xd4\x28\x98\x73\xda\xf6\x57\x07\x01\x88\xba\x40\x88\xb0\x62\x54\x83\x6b\x60\xb2\xac\x21\x39\x54\xb5\x17\x20\x44\x94\xb6\xdb\x2a\xc0\x05\x7a\xfc\x74\xbd\x90\xe4\x2f\x8d\x39\x97\xb8\x23\x51\xa2\x60\xa8\x4c\x49\x03\x51\x82\x7a\x40\xb9\x00\x40\x1a\xfb\xc1\x64\xb9\x67\x64\x85\x22\xc0\x31\xf1\xb9\xdc\xd6\x25\x70\xa2\x22\x13\x99\x2d\x74\xbf\x5e\xd7\x46\x2f\xaf\x6c\x87\x12\x05\x53\x2b\xda\x9c\x7d\x14\x74\x69\x3d\x05\x20\x36\x26\x81\x97\xb8\x01\x26\x8c\x62\x89\x82\x34\x01\x66\x06\x04\x40\x32\xf2\x35\x1a\x3e\x88\x47\x0b\x10\xc5\x37\x29\x7a\x10\x68\xdb\x0f\x04\xdc\x65\x88\x55\x15\xe3\x58\xce\x6c\x09\xd3\x60\xc2\xa4\x61\x6c\xa9\x6e\x57\x07\x18\x4b\x60\xcb\x6d\xed\x85\x17\x11\x44\x8f\x60\x1d\x84\x4b\xa3\xe3\x0b\x59\xa1\x55\xc3\x42\xc4\xa8\x05\xe7\xec\x83\x67\x54\xd1\x7c\x8d\xd3\x5e\x4f\x1c\x05\x13\xe6\x04\xc1\xd5\x6b\x5b\x01\xe2\x98\x5b\x30\x1c\xf5\x29\x5f\xb5\x39\x19\xf5\x31\xc6\x3f\x2f\xe9\xfd\x90\x18\x2f\x8d\x9b\x08\xbc\x97\x00\x0b\x8c\xba\x5d\xb5\x13\xc0\x48\x83\xf3\xa6\x00\x6d\x4b\x96\xb6\x00\xd7\x0f\xfe\x64\x21\x60\x9c\xcf\xd1\x9f\x3d\x7e\xce\x98\xc3\x5e\x30\xf9\x20\x10\xbc\xb6\x6b\x86\x92\x4b\x28\xb6\x68\x89\x82\xd6\x24\x65\x82\xcb\x60\x24\x97\x74\x01\xa4\x0c\x5f\x71\x98\x0f\x1e\x89\x17\xe7\xc0\xa4\xf1\x42\xe6\x6b\xc7\xa6\x1d\xce\x5f\x33\xb8\xf2\xd9\x1a\x04\x8c\xa0\x6c\x51\x00\x73\xf7\x63\x9e\x4c\xae\x92\xf4\xc9\x78\x79\x66\xcc\x58\xd0\xf4\xb9\x02\x98\x34\x05\x94\x3d\x4f\xd8\x08\xa0\xbf\x24\xa2\x37\x6d\xfd\x03\x91\x34\x96\x9f\xec\x67\xc6\xf5\xa7\x51\x4c\x3f\x31\x94\x5a\xe1\x17\x68\x7b\x43\x16\x34\x78\x00\x26\xcd\x02\x3b\x5c\x0a\xed\x3c\x0e\x84\x74\x37\xed\x6f\x87\xfc\x2c\x56\x21\x84\x2a\x56\x7d\xa7\x63\xcb\x6c\xfa\x44\x60\x43\x3a\xe4\x4e\x1c\x63\x5d\xe0\x94\xe5\x1c\x60\xe2\xf8\xe4\x48\xcd\x6e\x26\x28\x9e\xab\xbd\x81\x88\x35\x5a\xdd\x37\x34\x26\x16\xe3\x4a\x85\xb1\x03\x81\xff\xd1\x62\x35\xfa\xf6\x88\x10\x29\x19\xee\x35\xf7\xbf\x60\x04\x15\xb5\x04\x26\x4f\x03\xea\xa5\x90\xcd\x54\xd8\xb8\x1e\xe3\x77\x5a\x5d\xc0\x64\x7f\xe1\xee\xd4\xf5\xa2\x81\x00\x7d\x2e\x6a\xc8\xcb\x9c\x6e\x8d\xdb\x1d\x29\x63\x3f\x98\x3c\x6b\x30\x36\xb0\x43\xdc\x3b\x4d\x2b\x44\xcc\x91\xd0\x9f\x1c\x8b\xae\x8d\xbc\x5d\x94\x3f\x62\xcd\x81\xe3\xcc\xa8\x3a\xdd\x3c\x1d\x38\xf1\x20\xa0\x97\x42\x76\x1b\x23\xc9\x2b\x39\x70\xe1\xea\x29\x30\x05\x5e\xcb\xe5\xe8\x6d\x72\x21\xa2\x75\x9f\xbd\x35\xe2\x09\x96\x44\x3b\xd9\x20\x70\x5c\x1d\x6e\x06\xcc\xa1\x58\x7a\xda\x6d\x40\x8f\xd7\x74\x7c\x78\x89\x1a\x03\x4d\x79\x0b\x8e\x91\xbb\xae\x6d\xee\x9d\xee\xd9\x37\x70\xf0\x7a\xb8\x14\xd4\x11\x56\x31\x73\xd7\x76\x5b\xe3\xe0\xcb\x99\x8f\x31\x7a\x2c\xa5\x23\x49\x8c\xef\xd3\xc8\x27\x5f\xe2\x12\x78\x17\x0a\x90\x1b\xd8\x1e\xaa\x1f\xf6\x72\x95\x87\x78\x4d\x17\xa0\x65\x2c\x3a\x7e\x12\x11\xec\x8e\xc7\x09\x82\x98\xe9\x0d\x05\xd4\x35\x87\xb9\xf3\x6c\xdb\x13\xdc\xb1\x81\x0b\xf0\x2e\xcc\xb3\x7a\x14\xb4\x3d\x1f\x20\x84\x31\x7b\xe0\x72\xf7\x78\x5d\x18\x50\xf0\xd5\x50\x0c\x4c\x09\xad\xdd\xd5\x78\xb8\xd9\x95\x20\x93\xc8\xf6\x62\xec\x83\xdb\x35\x63\xb7\x84\xbc\xe0\x9d\xd0\x86\x1a\xc2\x9e\xf3\x4a\x99\x20\x31\x66\x37\x3d\x9c\xaf\xc9\x01\xf2\xd0\xdd\x11\x49\x38\xdb\x66\x90\xac\xc2\x58\x24\x91\x34\x2b\xad\x8a\xd3\xfd\x28\xc1\xfe\xd0\xc2\x54\x1c\xe0\x9d\x28\xeb\xb5\xa0\xfd\x11\x99\xe4\x4d\x56\xad\xfc\x29\x05\x4a\x43\x39\x40\x12\xe3\x31\xb3\x97\x80\x98\xab\x1b\x37\xe9\x9b\x59\x6e\xbe\xed\x7e\x94\xa0\xdd\xf7\xf4\x5b\xc8\x1c\x80\xf7\xe2\x52\x8e\x54\xed\xf2\xa0\x39\x16\xe1\x4e\x5d\x4f\x1f\x72\xea\xe5\x0e\xc0\x54\xf3\xfd\xab\xff\xb7\xa2\x0c\x51\x0f\x1c\xf9\xcd\x1c\x3a\x95\xa6\xe6\xdc\x4f\x4f\xdf\x72\x38\xf7\x9f\x4f\xc7\x9e\x80\x77\x63\x4d\xef\x88\x29\xa7\xc4\x24\x29\x48\x4a\x0e\xf3\x43\xbe\xe6\xa0\x84\x42\x42\xc2\xc0\x9d\xd9\x62\x16\xa2\x47\x60\x2c\x2c\xe6\x8d\x5b\x76\xc4\xfb\xbb\xfb\x99\x07\xbd\x2c\x70\xb9\x5c\x0e\x50\x09\xa0\x0a\x78\x3f\xee\xd6\x3d\x67\x1d\xbb\x3c\xa8\x93\xea\x30\x66\x26\x78\x55\x36\xf4\x5b\x6e\xd1\x1b\x24\xa5\x22\x43\x20\x7a\x06\xe2\x88\x94\xf9\x26\xae\x20\xdc\x98\x69\xbb\x2d\x3c\x3c\x3c\xb8\x5c\x5e\x18\x38\x01\xef\x88\x8b\x63\x97\xeb\xb4\xa3\xc2\xc2\x0a\x52\xfd\x7f\x7f\xfe\x80\x5b\x33\x2f\x50\x94\xc5\x7d\x07\x44\x84\xaf\x3c\x96\xb5\x67\x0f\x80\xd0\xeb\x23\x91\x8c\xa4\xcf\xdd\x54\x5c\xbe\x9f\xde\x72\x3f\xd3\x9e\xbe\x50\x8c\xc3\xb2\xef\x8a\x36\x98\x07\x3b\x87\xd6\xbb\x4d\x2a\x3a\xf9\xdd\xe2\xa6\x8e\x71\x45\xe1\x8c\x6e\x5d\xd2\xda\xf8\x5c\xc5\x48\xc0\x3b\x9b\x0e\x60\x55\xee\xf6\xd2\xb1\xf4\xec\x0f\x8d\xc4\x05\x63\xf2\x88\x08\x97\xbb\x77\x3a\x1d\x5d\x11\xe6\x1c\x33\x77\xe9\x18\x2e\x2f\x80\xf7\xa5\x34\x34\x13\xd9\xe9\xbb\x2a\x8a\xd7\x4f\xa6\x2d\x5e\x3a\x37\x32\x12\xec\x83\x28\xe1\xaa\x19\xeb\xf0\x23\xa0\xae\xff\x9c\xfd\x31\x5b\x89\x6a\xc6\xd9\x02\xad\x5c\xd1\xbf\xf8\x89\xf9\xbc\xa4\xc7\x86\x15\x8e\x10\xae\x7c\xb9\x36\xef\xfa\x3c\xf5\xe9\x61\x2d\xca\x60\xae\x02\xde\x9b\xf4\x50\x21\xb0\x29\x59\x03\x58\x11\xa3\x5b\x8b\x02\xc6\x9d\x81\x7d\x9a\xf9\x8e\xe9\x06\xce\xee\x5a\x6b\xa0\x2a\xaf\xaa\xe5\xd9\x1f\x3a\xb3\xb3\x3f\x0a\x05\xfd\x0f\xe3\xef\x85\x18\xdb\xcd\x13\xe2\x66\x91\x53\x09\x91\x75\x5d\xa2\x25\x1e\x61\xca\x56\xf0\x64\x69\xc0\x78\xcd\xa6\x21\xb6\xdc\xf2\x54\xe5\xc6\x54\xdb\x1a\xaa\x74\x67\x05\xc3\x38\x5d\x73\x33\x97\xaa\xda\x77\x81\xfa\x28\x8c\x60\xac\xf2\x63\x90\x0a\xe2\xcc\x9d\xb3\xbc\x70\xb8\x5b\x54\xb8\x2c\xd6\x61\xca\x27\x0e\xf0\x01\xe4\xe4\x22\xad\x10\x30\x11\xce\x03\xa4\x7c\x3f\xd5\x17\xae\xf5\x8c\xe5\xb4\xfa\xc1\xbe\x6e\xbd\x7b\xeb\x93\x17\xab\x71\xab\xfd\x3b\x9b\x45\x0c\x99\xc2\xa0\xfd\xb3\xe5\xe7\x3b\x76\x52\x52\x10\x77\x9a\x2b\xbc\xcc\x3d\x80\x8f\xe1\x04\x87\xec\x1a\x62\x0b\xc2\xb2\x87\xf0\x6b\xd3\x5b\x7d\x12\x18\x69\xbb\xed\x72\x18\xb8\xda\xdd\xdb\x44\x9f\x1e\xca\x04\xf6\xdd\x19\x91\x36\x37\xe3\x2a\x82\x3f\x67\x07\xec\xff\x09\x99\x5a\xc6\xea\x65\xa9\x3d\x4d\xd6\xc0\x07\x51\x50\x07\x05\x48\x0c\xb5\xef\x47\xdb\x58\x8e\xce\xcf\xcc\xb9\xfb\x79\x78\xc4\x98\x6d\x4c\xaf\x69\x24\x60\x4d\x80\x86\x00\xb5\x9b\x18\x94\xb9\xca\xac\xd5\xfc\x59\x63\x47\x4e\x1c\x1c\x93\xc2\xd2\x87\x09\x60\x78\x00\xad\x10\xe8\x91\x17\xc3\x0c\x61\x72\xee\x99\x2d\xb7\x3d\x5b\xd3\xf7\xe9\x75\xe2\xb9\x19\x68\x01\x0e\x13\xad\x38\x94\x55\xc6\x6b\x84\xc0\x5e\x24\xfc\xa9\x11\xfe\xab\x38\xf0\xfc\x6b\x06\x46\x3f\xce\x03\xf4\x18\x40\x29\x04\x2c\xa1\x70\x33\x94\x25\x7c\x6e\xfe\x77\xa3\x34\x1e\xc4\x35\x7d\x7f\xc9\x11\xb8\xbd\x39\xd4\x03\x1f\x25\x5a\x61\x4e\xc6\xaa\x9e\xeb\x2a\x85\x42\xa1\xf2\xb3\xcc\xc9\x6c\x51\x4a\x0e\xff\x72\x12\x78\x09\x3e\x88\x13\x39\x3e\x28\xc0\x8e\xdd\x8e\x9e\xd0\xdc\x46\x84\x89\xae\xb9\x67\x3e\xb9\xac\xef\xde\x31\xed\xba\xf0\x72\x04\xf3\x2b\x42\xc6\xe6\x5c\x64\xb5\xd5\x2c\xf2\x31\x4c\x70\x96\x8d\x05\x20\x5e\xf7\xfd\xe3\xfc\xb7\xfc\xd0\x1a\x5b\x81\xa8\x02\x3e\x88\xca\x50\x1d\xd0\x3c\xb2\xdf\xd0\x11\xce\x8e\x19\xa2\xf2\xd1\xca\x85\xdb\x35\x35\x6d\x30\xe3\x68\x9f\x16\x1a\xca\xba\x6e\x7e\x38\x2f\x52\x8e\x86\x6e\xee\x54\x97\x56\x8f\x23\x7c\xa0\x54\x6e\x9c\xdc\x03\xcd\x66\x5c\x2a\xae\xca\x68\x0d\x7c\x10\x39\xa3\x12\xb4\x19\x89\xd8\x7a\x41\x2b\xac\xc4\x08\x59\xe7\x94\xa8\xd7\x20\x5d\xd2\x18\x4c\x30\x1b\x59\x91\xe8\xb7\xc5\xa4\x8d\xc3\x8d\x7c\x5e\xba\xfa\xe3\x77\xb3\xf2\xfc\x0f\xc1\xe6\xf0\xa0\xca\x9e\x82\x0f\x22\x1d\x5b\xaa\xd3\x04\xb0\x89\x86\x62\xed\xfc\x38\xc8\x22\x4c\xba\x60\xc8\xf2\xdb\xe7\x55\x71\xdc\x75\x41\x29\x7f\xb4\xf4\xc7\x1c\x30\xf0\xf2\x36\x02\x44\x30\xe7\x00\x1f\x44\x89\xfb\xde\xa1\x08\x40\xd1\x40\x48\x6d\x7e\x5f\x09\xc7\x8b\xc5\xf8\xea\xd5\x72\x35\x23\xe8\xdd\xfd\x58\xa4\xa3\xe5\x3f\xee\xcd\x6e\xf9\x1f\x19\x69\x48\x1e\x5e\x56\xc0\x47\xc1\xeb\x03\x11\xaa\x00\x14\xf2\x99\xa4\xd0\xc5\xf2\x7b\x44\xc6\x09\xf0\xfd\x8f\xde\xc9\x17\xd8\xca\x0f\xdd\x25\x64\xfe\x8f\xb8\xeb\x00\x6a\x23\x4b\xd3\xde\xe0\x70\x70\x41\x09\x07\x55\x8b\x0d\x58\x0e\xd2\x59\x37\x9a\x72\x95\x5d\xa2\xa4\xd2\x05\xa6\xd6\x38\xe1\xec\xf1\x60\x39\xac\x4f\x98\xa9\x31\x5c\x61\xc3\x26\xdb\x35\x0b\x77\xe0\x70\x3e\x9c\x26\x98\xbd\x46\x1b\xb0\x4a\xb0\x01\x97\x2e\x57\x98\x64\x75\x3f\x0c\x52\xb7\x02\xa8\x69\x40\x2d\x1f\x93\x9d\x26\xe7\x50\xf7\x9e\xd4\x32\x12\x8f\x06\xdf\xd2\xdd\xfe\x2a\x3a\x10\xfe\x4f\xff\xff\xfd\xe1\xbd\xfe\x7b\xd3\x79\x35\x6d\x7e\xf4\xcf\x17\xcf\x5b\xbe\x20\x3b\xe2\xfa\xde\xe6\x3a\x52\x9a\x00\xd9\xe0\xad\x6f\x7f\xe9\xdf\xb3\xed\xf7\x8b\xd8\xc2\xb1\x83\xcf\xa9\x96\x05\x1f\x7d\x79\xde\x37\x5f\xb9\x1e\xa2\x68\x00\x40\xdf\xbc\x39\x73\x5e\xbe\xd1\x0f\x3a\x32\x04\x04\x3d\x9e\xa0\xa2\x04\x78\x5e\x3c\x3b\xe7\x3f\xce\x3e\xf1\xe2\x0f\x77\xec\xc3\x0f\x4b\x37\xed\xf9\x47\x15\x3e\xf6\x79\x73\x8b\x07\x32\xa6\x03\x9a\x0a\x47\x18\x40\xf7\x5d\xa7\x00\xa0\xff\x01\x69\xa0\xbf\xfd\x37\x3b\x7c\x7e\x6c\x20\x20\x1f\xea\x6b\xff\xe5\x0f\x2f\xbd\xf8\xe2\x8f\x77\xd6\x62\xf6\xa3\xa1\x43\xf5\xd3\xca\x18\xbd\x68\xd1\xa2\x55\xab\x56\x2d\x7a\xb9\x6c\x65\xf1\x00\x4b\x67\x8c\x67\xc3\x11\xa7\xcb\x6d\xb6\x1d\x8a\x52\x34\xcd\x32\x20\xe6\x0b\x42\xfb\x2f\xad\x7f\xaa\xba\x62\x2b\x89\x15\x42\xf2\xa1\xb6\x73\xef\xde\xe7\xbc\xc7\xf6\x37\x1e\x56\x29\x09\xfc\xcf\x82\xe2\xbe\x7e\x86\x61\xfa\xfb\xfb\x19\x8a\x06\x08\x34\x15\x8f\x26\x96\x99\xcd\x66\xe3\x91\x8d\x16\xbb\xce\x34\x38\x94\x8c\x70\xa5\xd0\x6a\x5f\x6f\x65\xd5\x6f\xb6\x9f\x5e\x77\xa6\x33\x28\x96\xc2\x0a\xc0\x2b\xee\x34\xc6\x80\x92\x80\x02\x1b\xb7\x5f\x41\x06\x53\x14\xcb\xd2\x10\x2c\x15\x8e\x0f\x27\x06\x93\x84\x49\x7b\xc4\x61\x71\x64\xa0\xd1\xf1\x44\x92\x02\xab\x61\x19\xd4\xb9\xb7\xb2\xbd\xe5\xe0\xb1\x60\x45\x1d\x19\x44\xcd\x90\xaa\x40\xb7\x31\xf6\xc8\xdf\x0a\x2d\x00\xf4\xf0\x32\xb7\xdb\x3d\x32\x3a\x36\x34\x34\x36\x9a\x4c\x11\x7a\x82\x17\xd2\x82\xc1\xa8\x71\xe4\x60\xd0\x27\xb8\x90\x27\x88\x08\xa8\xe8\xec\x44\xeb\x02\x2a\x76\x75\xfa\x50\x3b\xac\x2a\xf6\x6d\x7b\x6a\xcf\x35\xd9\x09\xb8\x0e\x9c\x3a\xe3\x91\x23\x76\xb3\x81\x20\x78\x9e\x17\x84\x74\x16\x82\xc9\x9c\x63\x40\xa3\x35\x85\xb9\x4c\x0e\xf0\x6d\xab\xe8\xed\xbc\xea\x45\x0c\x34\xf9\x7d\x68\x20\xa2\x26\xf6\xef\x6d\x90\xff\x2c\x78\x0d\xc3\xba\xc5\xcd\xf8\x66\x93\x90\xce\x87\x60\x75\x88\x14\x18\x4b\x00\xd5\x95\xa9\x83\xc9\xba\xba\x53\x9e\x96\x8c\x0f\xec\xde\xde\xa2\x2e\x01\x48\x03\x77\xcb\x3e\x0c\x5d\x0e\xc2\xe6\x8d\xe2\x07\x6d\xb7\x4e\x62\xc0\xe0\xce\x32\x60\x8e\x72\x6b\x45\xe1\x6f\xaf\xda\x59\x4b\x66\xf6\x9f\x37\xad\xdb\xba\x7f\x9f\x9a\xf6\x23\x0d\x94\x7f\x1c\x74\x14\x44\xcc\x0e\x11\x16\x8d\xd9\x50\x48\x81\xc9\xa6\xb1\x40\x07\x58\x42\x53\xb9\x53\x21\xcf\x8e\xdd\x3b\xba\xc9\xcc\xa2\xe9\xbd\xeb\x9a\xb6\xa1\xee\x46\x2d\xd4\x78\x2a\x37\x65\x6f\x5e\xff\xf5\x7f\xac\x92\x6d\x41\x48\x3f\xed\x42\x11\x20\x42\x73\xc8\x9a\x2e\xa0\x40\xd0\xd9\x35\x0e\x73\x1f\xd7\x43\x06\x44\x74\x6e\xad\xd8\x51\x4b\x8a\xeb\x7f\x2b\xe0\x46\x7c\x94\xb0\x55\x01\x9a\x86\x3c\x0e\xad\xbf\xf6\x83\xf5\xd5\x97\xe5\x3a\x16\xbb\x09\xe2\x36\x8b\x23\x0f\x93\x9c\x00\x69\xa1\xb6\x04\xc4\xf2\x1e\x1f\x27\xb7\x56\x5c\xba\x4a\xb6\x64\x86\xf5\xb5\x3b\x9b\xaa\x4e\x93\x07\x55\x92\x80\xd3\x17\x2a\x1f\x3b\x7b\x7e\x7d\xd3\xd6\xc0\x6f\xd6\x57\xca\xb2\x57\xf7\xe5\xe3\x80\x1d\x33\x6a\x0a\x19\xb0\x4f\x72\x02\xde\xcd\x80\xd6\xfc\x46\x90\xbc\x54\x71\xba\x96\xac\xf7\x66\x28\x38\xf0\xdc\x99\xaa\x1d\xaa\x30\x80\xd6\x35\x57\x57\xec\x3e\xb3\xfd\xc0\xfe\x9a\x63\xdb\x9e\x92\xe1\x29\x91\x55\x73\x19\x40\xbb\x78\x9d\x19\xbd\xf4\x47\x04\xae\x04\x02\x1f\xe2\x96\x16\xf6\x81\x9d\xdb\xaa\x76\xb5\xd7\x8a\x07\x63\x35\xc7\xba\xda\xd5\x08\x02\x74\x1b\xeb\x44\x1d\x1c\x36\x21\xb6\xbd\x07\x65\x78\x09\x4b\x59\x1f\x00\xa1\x31\x22\x2d\x08\x26\xab\xd9\x21\x72\x80\xa7\x03\xbd\x93\x43\x0a\x58\x00\x8f\xaf\x6e\xf7\xd6\xda\xab\x2d\x22\x05\xaa\xd8\x8f\x7a\xe1\x86\x4b\xb9\xb5\x85\x87\x2b\x66\xdd\x16\xaf\x64\x01\xe3\xe2\xf9\x34\x82\x90\x36\x58\xf3\xfd\xc0\x32\x51\x13\xf0\x63\x34\xea\x83\x27\x21\xd8\x7d\xa9\x6a\x57\x6f\xbd\xaa\x85\xc0\x81\x9d\x27\x2a\x7d\x35\x59\x39\xdc\x7f\xe6\xf2\x6c\x9b\xa2\x6f\x02\x7a\xd8\x44\xe4\x79\xba\x60\xca\x70\x90\xab\xff\x8d\x3a\x21\xfb\xd7\x61\x14\x00\x38\xfc\xbe\xd3\x55\x75\xbd\x87\xf7\xa9\x13\xfe\xe2\xcd\x7c\xb4\xbe\xd6\x0b\xdf\x7d\xb3\xbd\x69\xd6\x9b\x62\x16\x30\x74\x42\x9f\x31\xb1\x80\x03\x9d\xd9\x2d\x06\x83\x46\x63\x33\xc1\xbf\x23\x5c\x00\x0b\x00\x11\xf5\xb5\x5b\xaa\x76\x9d\xde\xa1\x9c\xff\xe3\xab\x9a\xb7\xfc\x2f\x9c\x2b\x06\xb7\x36\xed\xde\x30\xeb\x3d\x19\x73\x69\xca\x94\x42\x56\x63\x1c\x18\x6c\xc8\x11\x34\x1b\x1d\xe5\x36\x01\x12\x20\xd6\x80\x38\xda\xc9\x46\x38\xfc\xae\xdb\x51\xa3\x56\x19\xd4\x5e\xdd\x70\xfa\x37\xa7\x77\xed\xbe\x7c\xfe\xac\x0c\xf5\x70\x59\x88\x76\x89\x01\x80\x71\x90\x36\xe9\x20\x09\x59\x02\xf8\x10\x6a\x83\xa5\xb6\x29\x36\xee\x3b\xa6\x5a\x08\x1c\xd8\xda\x50\xfd\x83\x73\x7f\x7f\xed\x17\x32\xbd\x95\xf8\x06\x08\x8b\x3a\x27\x41\x82\x41\x87\x42\x20\x15\xa6\xaf\x78\xa6\xdb\x27\xaa\xa2\x04\xc8\x7b\x2d\x7a\xfe\x00\x70\xe2\x2e\x50\x48\x02\x84\xa4\x07\xa8\x3d\x0c\xf0\x1e\xae\x68\xd8\x20\x73\x1b\xc8\xe0\x2e\x80\x81\x88\xa0\x36\x40\x62\x9d\xa4\x8a\x40\xe7\xc2\xd5\xff\x2e\xf7\x24\xc4\x45\xcc\x4c\x40\x82\x1b\xf7\x4f\x49\x40\xad\x57\xe5\x61\xc8\x05\xb9\xaf\x86\xac\x04\x91\x99\x09\x48\x8d\xb0\xb8\x08\xe0\x13\x61\xe5\x71\xa0\xe9\x82\xdc\x0f\xc9\x97\x81\x70\xea\x41\x62\x60\xea\x42\xc8\xa7\xfa\x3c\x74\xd3\x63\xf2\x13\x20\xa4\x44\xc1\x13\x91\xc6\xc1\x8f\xd2\xa0\x95\x94\x58\xa6\xa7\x1a\xd0\x99\xd0\xee\x55\xb2\x87\x40\x48\x67\x16\x61\xb3\xd9\xac\x56\x9d\xc1\x60\x30\x89\x6c\xe4\xb9\x80\x93\x63\xf0\x5a\x10\xdd\x11\x54\x39\x09\xca\xbe\x25\xe1\x55\xe0\x34\x6b\xf2\x80\xda\x00\xbb\x51\x0b\xd9\x40\x54\x98\x72\x44\x08\x44\x9c\x1b\xf7\x60\x0c\x9c\x52\x37\x09\xd6\xaf\x6f\x90\xfd\x66\x48\x31\x70\x69\x1d\x93\x61\x11\xa9\x38\x04\x99\x80\x4e\x61\x40\x41\x60\x60\xb8\x52\x32\x18\xc0\x9e\x98\x52\x11\xfb\x76\xec\xa9\x98\xaf\xc0\x38\xd4\xe6\x90\x02\x62\x02\x21\xd3\x0f\x8c\xb2\xa0\x87\x7c\xa8\x65\xd0\xfe\x33\x28\x02\x64\xc6\x3c\x9a\x76\x9a\xb5\x8e\xe9\x60\x29\x77\x67\x64\x60\x88\x46\x0c\xa8\x59\x06\x61\xc3\x20\xf9\x23\x00\x0d\x44\xe9\x78\x89\xd6\x28\xed\x02\x0e\xb7\xcd\x90\x9d\x0a\x25\x00\x64\x20\x88\x5d\x14\x57\x2f\x02\x50\x0e\x90\x1f\x2b\x19\x40\x87\x4a\xb4\xf6\xa9\xdc\xff\x90\xdb\x6c\x35\xe4\xa6\xa3\x38\x03\xf5\x2a\xe7\x80\x06\x65\xee\x87\xae\xb8\xd1\x0f\xe8\x88\xdd\x58\x68\xbb\xc3\x68\xb6\x21\xe3\xf3\x93\x21\x62\xa0\xd4\xef\x7b\x38\x1a\xd8\x78\x4a\xb9\x07\x84\x17\xdd\x60\x40\xff\x6b\x90\x01\x0b\x32\xfd\x77\xaf\xb9\x71\xdb\x73\x0c\xd0\xdc\xd2\xa0\xe7\xa1\x68\xe0\x81\xad\x4a\x5e\x11\xff\x46\x1f\x88\x6b\x51\x01\x00\x4d\x4f\x1a\x4c\xb8\xed\x22\xf4\x43\x2c\x17\x6a\x26\xd5\xd6\x40\xb1\x0f\x50\xf2\x6a\xd4\x9a\x7e\xe0\x4a\xa3\x12\x10\xb3\xbd\xf0\xaf\x88\x51\x86\xa3\x5a\xc9\xa0\xea\x75\x60\x4d\xb0\x1a\x2e\xcd\x53\x10\x73\x41\x1c\x6f\x0b\xc5\x41\xb1\x2d\x99\xd7\x16\x24\xe3\x1c\xbd\xd6\xef\xc1\xae\x87\x29\x7e\x24\xa6\xf0\x23\xf2\xdf\x60\xd9\x91\xd4\x14\xe6\x1b\x32\x63\x72\xb7\x61\x82\x01\x7e\x18\x70\xe3\xbd\xa4\xba\x49\xc0\xeb\xad\x68\x50\xf8\x86\x78\xdf\x14\xa3\x11\x68\x7e\xf6\x7a\x44\x79\xde\x29\x91\xa0\x77\xb1\x1c\xd5\x41\x7a\xd4\x4c\x02\xf0\x55\xfc\xeb\x9f\x54\x96\x80\x57\xc0\xf0\x24\x02\x04\x93\x0d\x99\x8f\x60\x37\x14\x8c\x07\x46\x42\x1c\x28\xf2\xb5\xa9\x49\xc0\x2e\x54\x06\x2b\x8a\x95\x48\x04\x0a\xcd\xb7\x23\xf3\x11\x50\x37\x90\x0f\x9e\x77\xd2\x5c\xec\xf9\x8b\x35\x87\xd5\x3b\x0f\x50\x7c\x4b\xc8\x62\x10\x4e\xe7\xf9\x79\xda\x3a\x71\x62\xae\x31\x63\xb1\xa1\x1f\x0b\x73\xa0\xf4\x6a\x9b\x5a\x12\xb8\xe5\x04\x76\x10\xac\xc0\x45\xa9\x64\xea\xbe\xf6\xe9\xdc\xf0\x58\x48\x84\x05\x06\x00\x06\x5e\x18\x86\x4e\x70\x45\x1d\x27\x68\xec\xfe\x39\x92\x40\x85\xd1\x47\x8f\xf1\xa2\xf9\x49\x73\xfe\x41\xf9\xef\x32\x0a\x88\x3b\xc1\x20\x52\x82\xdf\x5f\x54\x21\x19\x5e\xec\x00\xaf\x97\x29\x4e\xc0\x2b\x20\x41\x88\xd2\x6f\x11\xcd\xcf\x17\x00\x1c\x7c\xca\xc9\x72\x6c\x47\xa3\xe2\x71\xe0\xfd\x55\x0c\x00\xea\xc6\xa3\x8a\xab\x60\x84\xc8\xd7\x3e\x11\x1a\x77\x5a\x0a\x82\x7e\x24\x0e\xb8\x90\xe2\x62\x78\xa0\x95\x63\x22\x34\xe8\x53\xd8\x09\xbe\x47\x33\xa6\x54\xda\x8a\xcc\x2f\x40\x52\x48\x4b\x82\x27\x5c\x61\x18\x07\x3f\x51\x94\x02\x6f\xdb\x38\x97\xd0\xbb\xc2\x80\xbe\xb9\x4a\xe9\x52\xe8\xaf\x8c\x9a\x93\x85\xe6\x6b\xcc\xd3\xd8\x2f\x08\x02\x21\xc0\x38\xa0\xd7\x76\x5f\x54\xee\x8a\x40\xdb\x15\x40\x19\x52\x44\x7a\x98\x06\x03\x8b\x95\x8d\x01\x66\x19\x36\x1d\xb3\x1b\x24\x09\x10\x0c\x36\xd8\x36\x0b\xfa\x64\x94\x86\x95\x61\xbd\x62\x6a\xd8\xb6\x94\x1b\x26\xb2\xaa\x0b\xd8\xb9\x0a\x12\xf0\xfd\x01\xc0\x14\x1b\xb5\x10\x46\xa3\xdd\x3e\xbd\x02\xa2\x4a\x01\xd6\x89\x9a\x72\x6d\x3a\x9d\xd2\x8f\x46\x00\x17\x6b\x6d\x54\x86\x82\xb6\x66\x20\x26\x68\x9e\x87\x4e\x70\x7c\x8d\x82\xb5\x50\x3f\xa0\x99\x78\xd4\x59\xec\x5a\xb2\xc4\x81\x78\x70\x38\x8e\x18\x24\x3f\x7e\x6d\x39\x22\x48\x9b\x95\x82\xa1\x10\xe0\x42\xab\x6b\xda\xbc\x8a\x38\x40\x54\x9f\x53\xdd\x21\x16\x0c\x28\xc8\xc0\x9a\x9b\x7d\x0c\x0d\x00\x7a\x64\x82\x09\xc7\x9d\xe5\xda\x23\x66\x29\xfb\x75\x59\xb1\x2c\x17\x3d\x84\xe0\x5d\x21\x8e\x1b\x47\x14\x28\xe0\x00\x23\x7c\xde\x3c\x02\xf4\x2d\x9a\xa3\x1c\xfe\x67\xc5\xf7\xce\x6d\xfe\xa7\x0b\x3f\x6f\xed\x28\x0d\xd1\x74\x64\x89\x94\x02\x58\x73\x4f\x92\x20\x0f\xc9\x51\x10\xe6\xb8\x50\x6b\x23\xa2\x40\x5e\x07\x88\x10\xf9\x8d\x18\x03\x8e\x2a\xbd\x2f\xeb\x87\x3f\xaa\xea\xf6\x93\x9e\xd5\x21\x8e\x72\xe9\xa7\x0c\x7f\xb3\x05\xcf\x11\x02\x91\xca\x52\xd0\x72\xb1\x51\xd6\x14\x40\x8f\xf2\xe9\x09\xa0\x13\x9a\xe5\x4a\x2f\x0b\x6a\x68\x42\xf7\x21\x3c\x9e\x22\x0e\x24\x70\x06\x04\x13\x0c\x7f\x11\x05\x1e\x22\x10\x02\xa2\x20\xd6\xd1\x2d\x5b\x5d\xe0\x6d\x19\xcf\x77\x00\x71\x36\xdd\xbf\x46\xe1\xcd\x99\x17\xaa\x3c\x99\xb9\x37\x59\x0a\x70\x06\xd0\x83\x44\x52\x6d\x22\xf2\x82\x10\xe0\xa8\x9e\xdf\x5f\x94\x25\x12\xbc\x87\x5b\x01\x3d\x88\x1c\xa0\xe0\xde\x32\x78\x45\xe1\xa5\x79\x17\xc4\xf5\xb9\x41\xc8\x00\x3d\x29\x0a\x04\xdd\x21\xcd\xfd\x2a\x49\x87\x4f\x50\x09\x7e\x28\x0e\x38\xb6\xa8\xb9\x6d\xf6\x91\xe0\x3d\xe5\x59\x88\x1c\xa0\x10\xc4\x18\x4d\x97\x29\x2b\x02\xb9\xf7\xa9\x20\x06\x38\x7a\x88\xc8\x37\xd0\x76\x72\xe3\xf4\x6d\x82\x40\x10\x63\x11\x9a\x03\xe3\xad\xf5\xb3\x8a\x04\x64\x3f\xd9\x81\x52\x00\x7e\x61\x05\x5c\x57\xf8\x4d\x02\x7b\x72\x6b\x12\x82\x50\x07\xd8\xd1\x3c\x06\xcc\xf9\x17\xca\xad\x82\x54\x8b\x30\x1a\xa5\x38\x8e\xe9\xf9\xc9\x6c\xdc\xc0\x5b\xef\x6b\x8f\xa1\x22\x70\x32\x52\x49\x0a\xac\x54\x92\x80\xa7\xab\x27\xd6\x25\xf9\x7c\xe3\x1c\x93\xe4\xef\x3f\x39\x52\x5e\x50\x26\x4b\x77\x49\xfa\x64\x22\x0c\x38\x7a\xbc\xf5\xea\x1f\xab\x06\xde\x53\x41\x72\x2d\x87\x1a\x34\x0c\x84\x0b\xc4\x5e\x56\x90\x80\x55\x70\x43\x41\x67\x40\x84\x2f\x10\xe2\xc2\x26\xbe\x50\xfe\x70\x09\xc4\x90\x22\x52\x43\x11\x16\xba\x41\x69\x73\xe3\x1f\x13\x0a\xde\xda\xa0\xaf\x97\xcd\x8d\xaa\xf1\x20\x28\x56\x54\x05\xf3\xd7\xe7\x7a\xba\x62\x5c\x5c\xe0\x27\xaa\x3f\xc9\x08\xc0\xc5\x60\xc4\x09\xdd\x00\x84\x7a\xde\xa8\xf9\xff\x72\xe0\xed\x0e\x04\xc8\x89\x1a\x08\x0b\x02\x06\xdc\x50\x72\x57\x8c\x28\x02\x22\x03\xbd\x19\x06\x04\x54\xfd\x15\x00\x8b\x80\xa9\xdd\x80\xe2\x60\x28\x74\xfc\x04\x72\xe0\x7d\xf0\xf4\xd7\xd9\x1e\x80\x0a\x48\x89\xc1\x87\x81\x18\xa2\xe9\xe5\x4a\xbe\x53\xe8\x42\x5d\x77\x60\x82\x81\x66\xc8\x80\xc1\x3a\x69\x52\x62\x31\x9a\xd2\x33\x43\xe0\x89\x64\x22\xce\x66\x38\x78\xa3\xf1\x62\x5b\xe3\x03\xd9\x7f\xd5\x07\xed\x6f\xa5\x41\x2e\x01\xe1\xd0\x3b\x01\xbb\x40\xc1\x5d\x29\xe8\x9d\x42\x85\x0c\x84\x97\x4c\x9a\x15\xa0\x46\x70\x66\x08\x82\x90\x82\xa1\x90\xe5\x20\xd4\xf3\xfc\xd5\xb6\x19\x1d\xc1\xdb\xd2\x19\x68\xf7\x91\x1d\x34\xe7\xd4\xa7\x05\xc9\xf8\x8a\x2a\xc9\xc0\xb5\x49\x2f\x16\xf3\x74\x85\x38\xd6\xa9\xd5\xda\xa7\x9f\x15\xe1\x30\x59\x6d\xe8\x86\x8d\xc8\x01\xe0\x00\x55\xd4\xfa\x13\xe8\x08\x92\xcf\x58\x79\xbd\xf5\x64\x10\x9a\xdf\x55\x04\xc0\x30\x21\xa4\xad\x52\x14\x08\x7c\x04\x28\x17\x05\x4f\xae\xbf\x50\xf8\x5e\x2d\x4f\xa0\x08\x80\xb0\x53\xa3\x35\x6b\xc5\x89\xc9\x03\x11\x20\xd8\xca\x35\xe5\x99\x50\x41\x1c\x24\x5d\xd1\x30\x8d\x1c\xa1\xb4\xf5\x8d\x96\x36\x8c\x05\x64\xfd\xe1\xab\xfe\x60\xd0\x43\x76\xf5\x50\x1c\x9b\x80\xf6\xa7\xdd\x5a\xa9\x86\x34\xa5\x28\x03\x68\x89\x9d\x27\x9f\x01\x9f\xbf\x87\xe2\x00\x15\xea\x8b\x44\x87\x9d\xc5\x25\x4b\x7e\xab\xd5\xe2\x49\x00\x87\xd1\x92\x97\x2d\x52\x84\x5e\x18\x4c\x44\x18\x00\x49\x88\x2d\xed\x59\xfd\x93\xfa\x36\x48\x43\x5b\x4d\x4d\x23\x84\xd7\xeb\x6d\xa9\xef\xf6\xfb\x49\x7f\x57\x6b\x11\xc5\x81\xf8\x08\x81\x18\xb4\x96\xbf\x66\x95\x8c\x02\x27\xcd\xce\x53\x6a\x81\x70\x25\xf6\x56\x25\xb2\xb9\x34\x46\x03\x0e\x22\xb3\x5f\x23\x14\x5d\xc6\xf3\x33\x39\x80\x4e\x53\x28\x16\x48\x13\x09\x13\x24\x21\x4c\x23\x16\x98\xf1\xd2\x9e\xd6\xe7\xdf\xf8\x65\x77\x2d\x04\xe9\x0f\x74\x35\x5f\xe9\x58\x3b\x4e\x01\x68\xfe\x10\x21\x7e\x73\xb7\x06\x3d\xc3\x29\x31\x96\x77\x2a\xd7\x18\x3e\x81\xbf\x5f\xd1\x47\x06\xaf\xb4\x76\x74\xf4\xf4\xf4\xac\x2d\x1d\x67\x00\x47\xc7\x47\x89\x19\x08\xc8\x16\xce\xbf\x35\x4c\x4e\x0c\xfa\xd4\xc8\x90\x33\x12\x66\x41\x86\x4e\x96\x62\x62\xb1\x50\x2c\x16\x63\x58\x80\x78\x09\x0f\x0f\x12\xb9\xef\x2c\x24\x1d\x16\xf4\x0c\xa7\x30\xb5\x0f\x28\xd7\x18\xfe\xa2\x7a\x92\x0b\x20\x04\x7d\x1e\x32\x0b\x7f\xfb\xea\x22\x1a\x85\xe9\xb4\x4e\x60\xb2\x6f\x94\xa8\x98\x90\x27\x10\xc2\xc8\xa0\xcb\x19\x89\x87\x19\x71\x65\x0d\xa0\x59\x26\x14\x49\x0c\xa6\xf2\xbf\xab\x80\xd2\xaf\x94\x13\x10\x2e\xd0\xbf\x48\x31\x15\x40\x6f\x5a\x96\x04\x54\xaa\x2b\xe3\x00\x44\x92\xb8\x13\x14\xfe\xee\xd2\x35\xb3\x60\x3b\xa2\x23\x20\x78\x53\x72\x64\x74\x70\x6c\x6c\x70\x74\x24\x99\x46\x77\xd7\x4d\x42\xe1\x7f\x43\x0c\x1c\xc2\x9c\x40\x7c\x94\x83\x5a\xa3\x94\x0a\x88\x2f\x58\x94\x86\xc7\xdf\x41\x71\xcc\x90\x5e\x5a\x0b\xcd\x05\xf7\x2b\xf0\x00\x29\xcf\x4e\x94\x84\x54\x8a\xcf\x20\x95\xb2\x96\x6f\x44\x9a\x81\x07\x92\x65\x4a\x27\x10\x4c\x4c\xff\x7c\xe5\x5e\x32\xba\x69\xe7\xf4\x0c\x04\xc9\xe6\x71\x8e\x76\x4a\x86\x81\xc1\x3e\x7d\xd7\xa0\x85\xfa\x86\xa5\xcd\xa9\x0e\x23\xcc\xe5\xe2\x23\xfd\x53\x10\xd0\x37\x47\x29\xbc\xd9\xd4\xb0\xee\x39\x5f\x20\x38\xad\x13\x78\x7a\x68\x2e\x62\x22\xa4\x22\x60\xfa\x03\x66\x2d\xcc\x91\xba\x42\x77\x37\x64\xab\x6c\x44\x18\xe6\x4a\xb8\x13\xa0\x10\xb8\x39\x47\x31\x3c\xb6\xf9\xc4\xae\x5a\xcf\x73\xc1\xe9\x9d\xe0\x0a\xac\x92\x07\xf5\xd2\x11\x80\x60\x99\xba\x6a\x76\x5b\xb0\x6a\x4a\x34\xf5\xd0\x64\x4b\xb5\x1a\x6c\xaf\x83\x28\x82\x0b\x94\x7c\xbf\xde\x9e\x3d\x67\x9a\xce\x14\x12\x80\xc1\xd3\x5e\xc4\xd1\x09\x22\x35\x45\x04\x1c\x72\xe4\xf0\x9a\x41\x82\x00\x87\xdd\x34\x45\xe5\x80\xcb\x80\xc8\x00\xfc\x87\x02\x6a\xf4\xc3\xac\xa2\x03\xe2\x73\x27\x2a\xb7\x6c\xdb\xd6\x35\x3d\x03\x99\xb6\x25\x92\xe6\xa5\x23\x40\x4a\x04\xdc\xb9\x32\x11\x77\x9b\xf2\xc9\x89\xc3\x94\x63\xe0\xb7\xb6\x3c\x27\x20\xa2\x03\x8a\x19\xff\xd6\xdb\xaf\x3f\xfe\xc4\xee\x5e\x72\xdb\xb6\xde\xc0\x0c\x90\x08\x03\x73\x1e\x01\x36\x29\x02\x30\x19\xd4\x9d\xc4\x29\xcb\x67\x60\xa3\xc6\x9d\x14\x26\x3c\x40\x29\x0d\xfc\xc6\x23\xb7\x6e\xdf\xbe\xb3\xcb\x43\x7a\xba\x70\x17\xc0\xe0\x09\x4e\x11\x06\x26\x3b\xde\x3a\xe3\x04\x60\x29\x52\xc8\xf1\x36\x69\xe4\x2e\xe4\x18\x70\x58\x4e\xde\xaf\x14\x88\x84\x32\x75\xd0\x9f\x3f\x72\xfb\xd6\xc0\xdd\xbe\x7b\x2d\xa4\xc7\xe3\xd9\x3e\x33\x01\x28\x0c\x58\x2e\x57\x14\xe1\x11\x80\x42\x5d\x22\x0b\x88\x1f\x35\x2e\x1d\x1a\x71\xde\x82\x31\x90\x2f\x86\xfc\x18\xfb\x3d\xf9\xcd\x9f\xff\xce\xad\x5b\x7d\xef\xbe\xf7\xde\xfb\xdc\x5a\x12\x32\xd0\xd9\xbb\x3d\x18\x98\x11\xa8\x24\xa0\x86\x88\x14\xfe\x49\xe6\x9f\xa1\xe1\xd2\x8e\x3b\x87\x60\x93\xaa\x1f\xf3\x36\xbb\x89\x62\x98\x4a\xb2\xf2\x37\xc4\xcb\xef\xdc\x1e\x78\xf7\xbd\x0f\x3e\xfc\x40\x77\x07\x84\xae\xd4\x76\x93\x4d\x67\xba\x03\x33\xc3\xe3\x5b\x8b\x2a\x63\xbd\x90\xef\xe0\x98\x0a\xe2\x04\xe0\xdc\x98\x50\x0f\x2d\x1e\xbd\x4b\xca\x8a\xc6\x61\x33\x09\x68\xbb\xc9\x4d\xb9\xc7\x81\x1f\x7d\xcc\xde\x85\xe6\x7f\x08\x09\x78\x7d\x9c\xa3\x4b\xb7\x55\x9d\xa8\xbc\x44\x06\x66\x46\x90\x5c\x1d\xe3\xa8\x04\x4f\x88\x9e\xec\x40\xc0\x55\x10\x33\x07\xfd\x13\x96\x0a\x71\xd6\x70\x61\x75\x5b\xd1\x03\x9d\x32\x8f\xc7\xe7\xdd\xba\x75\xf4\x93\x4f\x3e\x44\xf8\xe0\xd3\xbf\xf4\x2f\x05\x80\xfe\xf1\xee\x86\xca\xdf\xcc\xc8\x00\x82\x3f\x50\x0a\xb8\xf0\x10\x4f\x08\x93\x25\x00\xf9\xb3\x14\x01\x1b\xed\x26\x29\x33\x51\x3d\x84\xc5\x07\x6a\x30\xc5\x7c\xa0\x4d\x12\x71\x79\x0f\xc9\xca\x6e\xdd\xba\xfb\xde\x87\x59\x7c\xf2\xd9\x9f\x5c\xbc\xf4\x3a\xe8\x2f\xfb\xe7\xf5\x0d\xeb\x76\x3c\x10\x03\x3e\xb2\x35\xc6\x81\x90\xcb\xa0\x27\x78\x68\x84\xdd\x6e\x34\x1a\xb5\x19\x18\xcd\x29\x41\x62\x5a\x30\x85\xab\x9b\xec\x16\x51\xec\x90\xdc\x4b\x9d\xcd\x22\x29\xb0\x86\x8e\xcb\x69\xff\x5f\xdf\xbb\xad\xfb\xe4\xc3\x1c\x01\xc5\xbf\xde\xb2\xf9\xdc\xe7\x6f\xc1\xa3\x32\x8c\x01\x49\xf8\x83\x99\xc9\x59\xd4\x35\x82\xe6\x86\xcb\x96\xb8\x4a\x8a\x9d\xc3\xc3\x68\x88\x36\x9a\x22\x52\x18\x01\x12\x75\x22\x72\x1f\xa9\xf4\x29\x18\xb4\x13\x0c\x18\x1d\x8c\xac\x1a\xf0\xc5\xc7\xc5\xf0\xf3\x17\xf1\xde\xf5\x13\x97\xcf\x8a\xd3\x11\xc8\x00\xae\x03\x12\x4e\xd0\x5b\x8a\x66\x5a\x34\xc3\x30\x14\x4b\x83\x2c\x38\x00\xe8\xb0\x33\xa9\xe7\x71\x02\xf0\xf9\x32\xf6\x4f\xd8\xf5\xb4\x93\x39\xfb\xb5\x71\xf6\x4b\x39\x03\xe0\xf6\x9d\x0f\x3e\xb8\xef\x00\x1f\xbc\x7e\xed\xd9\xfb\xc3\x81\xdd\x0d\xd5\x5b\xbb\x03\x0f\x48\xc1\xf6\x8e\xa5\x31\x96\xa6\xd1\x7c\x67\x69\xe9\xda\x8e\x8e\xd6\x8e\x9e\xa2\x10\xcb\x71\xd4\xf0\x08\xc1\x63\xf9\x0e\xc2\xa2\xc3\x82\xc0\xa8\x91\x56\xcf\xdc\x19\x95\xb6\x3c\x44\x2f\x97\x57\x01\x6e\x1f\xfd\xf0\x93\x4f\x3e\xf9\xe0\x83\x4f\xde\xfb\xe4\xdd\xfe\x6f\xe6\xbf\x6b\xf2\x72\xc3\xe6\xbd\x64\xf0\x41\x29\x20\x83\xdb\x9b\x9b\x9b\xb7\xb7\x7b\x32\xe3\x33\x38\xea\x25\x3d\xcd\x3d\x21\xc0\xd1\x91\x51\x82\xc7\x09\xc0\x3d\x5d\xd0\xfd\x6e\x9a\x04\x2a\xe8\x32\xf6\x97\x50\x8c\xcc\xbd\xe0\xe2\x47\x5e\xbf\xf3\x55\xf1\xa7\x77\xef\xbe\x7f\x7c\xe0\x8b\x79\x85\xe9\xb1\x69\xd3\x9e\x2d\xa4\x2f\xf0\x80\x08\xfa\x10\x82\xc1\x7c\x56\x3c\xab\x97\xd2\x90\x02\xe4\x05\x79\x04\x60\x03\x00\xec\x1f\x33\xee\x81\x8b\x84\xf9\x38\x5b\xac\x40\x2b\xf8\xbd\x6f\x3e\xf2\xc5\x17\x8f\xbc\xf3\xf9\x93\xd8\x8b\xe6\xce\x6d\x6a\xd8\xe5\xf3\x04\x66\x01\x38\x46\x2c\x82\x14\x44\x11\x05\xa2\x15\x98\x0b\xe0\xb9\xd0\xf2\xda\x54\x0c\xd8\xcc\xc3\x12\x1f\xbf\x92\xe3\x81\x0b\x55\xbd\x64\x40\x06\x0a\x22\x83\x04\x91\xca\x11\x90\xcd\x96\x26\x3d\x41\xf0\xf9\xa9\x52\xc8\x15\x84\xe8\x89\x15\x21\x83\x02\x25\x8c\xb2\x5f\xcf\x51\x1b\x1b\xaa\x2f\xac\xdb\xda\x1d\x9c\x25\x05\xcf\x23\x0a\xe2\x89\x24\xaf\x37\x64\xcb\x04\x63\xf9\x92\x92\x92\x84\x33\xe1\x1a\xb3\xa6\x88\x3c\x89\xd0\x6d\xbc\x3f\x50\x12\x04\x93\x29\xcd\xe7\xfd\x23\x91\x40\x07\x02\xaa\xe3\xf1\xf5\x0d\x4f\x9d\xf1\xf9\x03\xb3\xa5\xa0\x94\xe2\x38\x36\x14\x4d\x94\xc0\x32\x21\x1a\x67\x58\x1a\x22\x73\x2c\x10\x8e\x24\x46\x78\x22\x55\x10\x04\x76\xad\xb6\x64\x38\xcc\x30\xe8\xc8\x60\x94\xcf\x72\xc0\xa7\xc2\xcc\xcb\x73\x1e\x02\xfe\x16\x0a\x01\x5c\x29\xfc\xa0\x5a\x28\x9d\x28\x7b\x42\x59\x8b\x69\x00\x41\x0d\x14\xdf\x9c\x0b\xf1\xca\x75\x06\x00\x36\x04\x39\xd0\xf3\x29\x74\x28\x74\x12\xa5\x7a\xbb\xb3\x9f\x46\x65\x05\x85\xbe\x20\xe4\x84\x04\x11\xc4\x48\x1c\xcc\x9d\xf3\x70\xf0\xaf\x95\x0d\x9b\xcf\x04\xc8\xc0\x2c\x29\xa8\x3d\x75\xa9\xe2\xde\xbd\x7b\xaf\x5f\x2f\xbe\xb9\xb2\x6c\x42\xcb\x17\x2d\xfe\xe6\x71\x0a\x72\xe0\x1c\x4a\x22\x3b\x6d\x5a\xed\x92\x61\x06\xb0\xc7\x57\x2e\x5e\x31\xff\xcf\xcb\xe6\x1e\xa7\x10\x41\x91\x48\x88\xc5\xae\x08\xa9\x87\xa7\x9b\x36\x35\x54\xec\xec\x9c\x4d\x1c\x04\xfd\xdd\xdb\xea\xd6\xc1\xf7\x8a\x9e\x7b\x7a\xaa\x85\x0e\x0b\x8e\xf6\xa3\xb7\x5a\xc4\x23\x91\x48\x5f\x3f\x4b\x5f\x5f\x39\x41\xd0\x9a\xe5\xc5\xfd\x00\xe2\xff\xb8\xa7\x63\x9d\x84\x81\x30\x80\xe3\xe7\xa0\x3e\x02\x5d\x71\x31\xae\x0c\xec\x22\x95\xb6\x25\xd7\x52\xc5\xb6\x97\xd2\x8d\x41\xd3\x20\xde\x70\x0e\xd8\x84\x81\x05\xb0\xa1\x4d\x51\x06\xe9\xb1\x15\x99\x20\x8d\x6f\xe0\xc4\xe8\x1b\x79\xc4\x49\x13\x89\xb2\xa0\xfd\xad\x37\xfd\xbf\xef\xbb\xb7\x65\x0e\x6c\xcd\xc1\x8d\x84\xa1\x1a\x46\xf1\xc6\x1f\xc0\x0e\x75\xcb\x85\x88\x98\xe0\x1b\x39\x6e\x79\xc7\xee\x9e\x1d\xfe\xeb\x6e\x16\x7c\x96\xe1\x0e\x8f\xf6\xf6\xc1\x56\xbd\xf8\x0e\x76\x48\x48\xe3\x64\x83\xe5\x47\xa3\xae\x8a\x02\x96\xdf\x7f\x04\xeb\x64\x76\x38\x2e\x9b\x07\x7f\x94\x39\x44\xb7\x8e\xaa\xb1\x65\xfe\xb2\x7e\xa0\xe9\x22\xc6\x48\xf1\xcc\x16\xf8\xd7\x5a\x1d\x82\x30\xe4\x2b\x4d\x1a\xd9\xc9\x8f\xe2\xed\x28\x1a\x5d\xe8\x32\x5b\xbe\x23\x97\xda\x20\x05\x4c\xcf\xc2\x81\xa5\x56\x9a\x93\x19\x5d\x3b\x84\x64\x12\x47\x74\x50\xed\x12\x11\x06\x10\x59\xc2\x22\x0f\x52\xe2\xbe\x64\x40\x8c\x15\x43\xaf\x35\x9f\x59\xa3\x3d\x49\xbe\x96\xdb\x31\x9d\xc5\xc9\x5c\xeb\x11\x59\xc1\xac\x5e\x14\xca\x05\x90\x26\xc5\x8e\x2f\x23\x56\x66\xf1\xa4\x57\x0b\xcf\x46\xab\x62\x1a\x31\x94\xc6\xb6\x3d\x98\x57\xb5\x6e\x83\xf0\x12\x74\x5d\x8c\x14\xc3\x7f\x38\x01\xe9\x93\x37\x4b\x44\x44\x30\xc0\x10\x49\x22\x6f\xa8\x44\xff\x40\x54\x5e\x16\x15\xc8\x1e\xb0\xcb\x06\x24\x4d\xdb\x45\x90\x5a\xc7\xe6\xd8\x1f\xae\x6a\x31\x13\xac\x60\xd6\x8d\x21\x84\xc8\x92\x87\x4a\x43\x9b\x5f\x92\x05\x48\xbb\xab\x76\x67\x31\xae\xfb\x9e\x40\x18\x41\xf0\xfc\xfa\xb4\xdc\x3f\x2d\x80\x27\xeb\xfc\x7a\xf6\xde\x8e\x1d\x82\x44\x10\x84\x61\x18\xfe\x2f\x78\x82\xdb\x2c\x1a\xb5\x97\x41\x41\xcc\xb6\xb5\x1f\xd8\x8b\x76\xc1\x24\x68\x12\x93\xd8\xc1\x6e\xb4\x68\x87\x93\x55\x30\x88\x3d\x2f\xb8\xeb\xdc\xb8\xe3\xff\xdd\x0c\xb8\x0c\xc8\xf4\x8e\xe1\x9b\xa7\xa7\x37\xbe\x27\xbb\x69\x22\xa4\x1e\xea\xfb\xeb\x8b\xb5\xa6\x99\x0a\xa5\xcf\xc9\xed\xd9\x5d\x5e\xe5\xef\x9b\x42\xa9\x0b\xa1\xfd\x78\x69\x9e\xb7\x8e\x56\x85\x92\xe9\xab\x37\xeb\xae\x8e\x67\xf5\x93\x50\xba\x71\xde\x0f\x73\xb7\x3f\xab\x1f\x85\xd2\xb2\x55\xa8\x5f\xa4\xf6\x90\x35\x40\x8a\x00\x34\x56\xf6\x80\x37\x40\xe6\xab\x6f\xee\x00\x8a\x91\x3b\x00\xd8\x03\xe8\x60\x84\xd2\xd8\x2a\xb2\xf8\xd3\x91\x06\x70\x8a\xcc\xbf\x5e\x0a\x25\x63\x91\x29\x96\xc2\xa9\xfb\x5a\x28\x00\x3f\x5f\x11\x4e\xd3\x50\x79\x20\xae\x27\x21\xb5\x63\x9d\x22\x0e\xf6\x57\x58\x99\x70\xea\xe1\x3a\xa1\xb5\xdd\xf7\xe7\xce\x08\x31\x13\x82\x11\x66\x7b\xa3\xd9\x90\xa2\x28\x8a\xa2\xf8\x5f\x7f\x88\x5b\x0e\x55\x77\x19\xdf\x0c\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\x01\x00\x00\xff\xff\x01\xf6\x4a\x2b\xaa\x2e\x00\x00") 74 | 75 | func dataBalletEgon256PngBytes() ([]byte, error) { 76 | return bindataRead( 77 | _dataBalletEgon256Png, 78 | "data/ballet-egon-256.png", 79 | ) 80 | } 81 | 82 | func dataBalletEgon256Png() (*asset, error) { 83 | bytes, err := dataBalletEgon256PngBytes() 84 | if err != nil { 85 | return nil, err 86 | } 87 | 88 | info := bindataFileInfo{name: "data/ballet-egon-256.png", size: 11946, mode: os.FileMode(438), modTime: time.Unix(1534785449, 0)} 89 | a := &asset{bytes: bytes, info: info} 90 | return a, nil 91 | } 92 | 93 | var _dataClickPng = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xea\x0c\xf0\x73\xe7\xe5\x92\xe2\x62\x60\x60\xe0\xf5\xf4\x70\x09\x62\x60\x60\x6a\x60\x60\x60\xcc\x60\x01\x0a\x30\xa8\xaf\xfc\xd1\x03\x14\xb0\xf6\x74\x71\x0c\xa9\xb8\xf5\xf6\xae\xa3\x17\xb3\xa1\x48\xc3\xc3\x05\xef\xb8\xae\x38\x57\x4c\x10\x2c\x4a\x29\x57\xae\x60\xe4\x0f\x5e\xe0\xfd\x94\x93\x67\x8a\x5d\x77\x62\x8e\x4a\xcf\xaf\x5a\xed\x95\x1b\x65\xeb\x66\x7d\xe2\x58\x2c\x56\xa7\xcf\x80\x1d\x64\x15\xd5\xff\xfb\x11\x72\x2d\x53\x7f\xed\x7c\xdb\x6f\xf1\x77\xa7\xb9\x18\xcd\xb5\xaf\xd3\xc8\x7d\x3d\xed\x58\xfa\xf1\x7b\xab\xa5\x1f\x2c\x53\x58\xd0\xc0\xc4\xd5\xd1\xc0\xf4\xef\x69\x43\xf9\xd6\x6b\xcf\x9f\x3c\x34\x79\xbe\xcc\x75\xe9\xe1\xcf\x8b\xa6\x7c\xda\x57\xf3\x35\xe8\xd7\xfa\x3c\xe5\x6f\x82\x81\x93\xa6\x77\x24\xcf\xeb\xfb\xbc\xe8\x9a\xc2\xdd\x69\xea\x2f\xee\xad\xde\xdc\x5a\x3f\x0d\xa2\x8f\x4b\xe3\xc1\xeb\xa7\x73\x7e\x6f\xf3\x39\xd8\xbc\x75\xbd\x1e\xff\xf7\xbf\xc7\x9f\xd5\x36\x97\x1c\x2f\xb1\xa9\x98\x90\x57\xbf\xfa\xbd\x52\x6e\xe2\xfc\x46\xa8\xc2\x84\xcf\x2e\xbb\x6a\xdb\x8f\xbd\xfd\xfe\x6e\xff\x8e\xec\x3b\x5b\x37\xfa\x7c\x30\xdd\xf9\xbf\xc8\xe8\xdc\xf9\x29\x72\xa7\xbb\xd9\xf8\x2f\x4d\xef\x7e\xf4\xa4\x62\xe7\x96\x37\x4a\x49\x51\xf6\x1e\xef\x1f\xa7\x44\x55\x66\xbb\xff\x3a\xe8\xfc\x79\xe6\xa7\x45\x11\x37\x0e\xd9\x31\xb3\x8a\x08\x30\xb2\x8a\x04\x38\x00\x95\x66\x56\x4a\xcd\x9a\xbe\xf4\xad\xf1\xf3\x3f\x77\xbf\x30\xbd\x7e\x77\xcb\xa3\xfb\xd3\xd4\x2d\xe2\x96\x13\xb7\xb5\x42\x15\x1d\x38\x6b\xe0\x91\xbe\x51\xe3\xe4\x0d\xc7\x2a\xf9\x74\x9e\x4b\xdb\x36\x34\x30\x29\x81\x7c\x7a\x62\x59\xdd\xcd\x82\x3b\x9d\x25\xbf\x5f\xdd\x32\xde\x24\xfc\x52\x90\x05\x68\x1e\xa3\x20\x10\x77\xa6\xdc\xfa\x67\x7b\xe9\x67\x68\xf8\x1c\x8f\xe5\xae\xf7\x1a\xe1\x1a\x38\x70\x04\x2c\x10\x7c\xe0\xbe\x1a\xb5\xfc\xd0\x57\xa5\x47\x0f\xe4\x3f\xff\x65\xb7\x54\x00\x7a\x71\x41\x03\xc7\x09\x9b\xf3\xfe\x5b\xaf\x1d\xfa\xfa\x4d\x30\x91\xfb\x37\x8f\x8b\x03\x23\xc8\x45\xff\xe7\xdf\xb5\x39\xbf\xf5\xda\x71\x90\xea\x0c\x84\x30\xeb\x93\xdf\x65\x82\x86\x95\xec\xb7\x0e\x71\x00\xb5\x82\x2c\xb4\x98\xe8\x06\x95\x12\x11\xf8\xee\xa1\x0c\x89\x1c\x89\x64\x89\xb3\xf1\x6f\x1d\x1c\xc0\xbe\x67\x11\x11\xc0\x74\x4a\xc2\x69\x3b\x86\x86\x3e\xb7\x19\xaa\x8b\x7d\xea\x40\x7c\x4f\x57\x3f\x97\x75\x4e\x09\x4d\x80\x00\x00\x00\xff\xff\x61\xbc\xa2\x30\x74\x02\x00\x00") 94 | 95 | func dataClickPngBytes() ([]byte, error) { 96 | return bindataRead( 97 | _dataClickPng, 98 | "data/click.png", 99 | ) 100 | } 101 | 102 | func dataClickPng() (*asset, error) { 103 | bytes, err := dataClickPngBytes() 104 | if err != nil { 105 | return nil, err 106 | } 107 | 108 | info := bindataFileInfo{name: "data/click.png", size: 628, mode: os.FileMode(438), modTime: time.Unix(1534785449, 0)} 109 | a := &asset{bytes: bytes, info: info} 110 | return a, nil 111 | } 112 | 113 | // Asset loads and returns the asset for the given name. 114 | // It returns an error if the asset could not be found or 115 | // could not be loaded. 116 | func Asset(name string) ([]byte, error) { 117 | cannonicalName := strings.Replace(name, "\\", "/", -1) 118 | if f, ok := _bindata[cannonicalName]; ok { 119 | a, err := f() 120 | if err != nil { 121 | return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) 122 | } 123 | return a.bytes, nil 124 | } 125 | return nil, fmt.Errorf("Asset %s not found", name) 126 | } 127 | 128 | // MustAsset is like Asset but panics when Asset would return an error. 129 | // It simplifies safe initialization of global variables. 130 | func MustAsset(name string) []byte { 131 | a, err := Asset(name) 132 | if (err != nil) { 133 | panic("asset: Asset(" + name + "): " + err.Error()) 134 | } 135 | 136 | return a 137 | } 138 | 139 | // AssetInfo loads and returns the asset info for the given name. 140 | // It returns an error if the asset could not be found or 141 | // could not be loaded. 142 | func AssetInfo(name string) (os.FileInfo, error) { 143 | cannonicalName := strings.Replace(name, "\\", "/", -1) 144 | if f, ok := _bindata[cannonicalName]; ok { 145 | a, err := f() 146 | if err != nil { 147 | return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) 148 | } 149 | return a.info, nil 150 | } 151 | return nil, fmt.Errorf("AssetInfo %s not found", name) 152 | } 153 | 154 | // AssetNames returns the names of the assets. 155 | func AssetNames() []string { 156 | names := make([]string, 0, len(_bindata)) 157 | for name := range _bindata { 158 | names = append(names, name) 159 | } 160 | return names 161 | } 162 | 163 | // _bindata is a table, holding each asset generator, mapped to its name. 164 | var _bindata = map[string]func() (*asset, error){ 165 | "data/ballet-egon-256.png": dataBalletEgon256Png, 166 | "data/click.png": dataClickPng, 167 | } 168 | 169 | // AssetDir returns the file names below a certain 170 | // directory embedded in the file by go-bindata. 171 | // For example if you run go-bindata on data/... and data contains the 172 | // following hierarchy: 173 | // data/ 174 | // foo.txt 175 | // img/ 176 | // a.png 177 | // b.png 178 | // then AssetDir("data") would return []string{"foo.txt", "img"} 179 | // AssetDir("data/img") would return []string{"a.png", "b.png"} 180 | // AssetDir("foo.txt") and AssetDir("notexist") would return an error 181 | // AssetDir("") will return []string{"data"}. 182 | func AssetDir(name string) ([]string, error) { 183 | node := _bintree 184 | if len(name) != 0 { 185 | cannonicalName := strings.Replace(name, "\\", "/", -1) 186 | pathList := strings.Split(cannonicalName, "/") 187 | for _, p := range pathList { 188 | node = node.Children[p] 189 | if node == nil { 190 | return nil, fmt.Errorf("Asset %s not found", name) 191 | } 192 | } 193 | } 194 | if node.Func != nil { 195 | return nil, fmt.Errorf("Asset %s not found", name) 196 | } 197 | rv := make([]string, 0, len(node.Children)) 198 | for childName := range node.Children { 199 | rv = append(rv, childName) 200 | } 201 | return rv, nil 202 | } 203 | 204 | type bintree struct { 205 | Func func() (*asset, error) 206 | Children map[string]*bintree 207 | } 208 | var _bintree = &bintree{nil, map[string]*bintree{ 209 | "data": &bintree{nil, map[string]*bintree{ 210 | "ballet-egon-256.png": &bintree{dataBalletEgon256Png, map[string]*bintree{ 211 | }}, 212 | "click.png": &bintree{dataClickPng, map[string]*bintree{ 213 | }}, 214 | }}, 215 | }} 216 | 217 | // RestoreAsset restores an asset under the given directory 218 | func RestoreAsset(dir, name string) error { 219 | data, err := Asset(name) 220 | if err != nil { 221 | return err 222 | } 223 | info, err := AssetInfo(name) 224 | if err != nil { 225 | return err 226 | } 227 | err = os.MkdirAll(_filePath(dir, path.Dir(name)), os.FileMode(0755)) 228 | if err != nil { 229 | return err 230 | } 231 | err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) 232 | if err != nil { 233 | return err 234 | } 235 | err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) 236 | if err != nil { 237 | return err 238 | } 239 | return nil 240 | } 241 | 242 | // RestoreAssets restores an asset under the given directory recursively 243 | func RestoreAssets(dir, name string) error { 244 | children, err := AssetDir(name) 245 | // File 246 | if err != nil { 247 | return RestoreAsset(dir, name) 248 | } 249 | // Dir 250 | for _, child := range children { 251 | err = RestoreAssets(dir, path.Join(name, child)) 252 | if err != nil { 253 | return err 254 | } 255 | } 256 | return nil 257 | } 258 | 259 | func _filePath(dir, name string) string { 260 | cannonicalName := strings.Replace(name, "\\", "/", -1) 261 | return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) 262 | } 263 | 264 | -------------------------------------------------------------------------------- /ep02/data/inject.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import "github.com/klauspost/gfx" 4 | 5 | func init() { 6 | gfx.AddData(Asset) 7 | } 8 | -------------------------------------------------------------------------------- /ep02/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Episode 2 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | Canvas not supported 14 |
15 |
16 | 17 | 18 | 19 |
Full Screen
20 |
21 | 22 |
Starting
23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /ep02/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "image" 5 | _ "image/png" 6 | "math/bits" 7 | 8 | _ "github.com/klauspost/gad/ep02/data" // Load data. 9 | "github.com/klauspost/gfx" 10 | ) 11 | 12 | // Generates binary data. 13 | // To install go-bindata, do: go get -u github.com/jteeuwen/go-bindata/... 14 | // 15 | //go:generate go-bindata -ignore=\.go\z -pkg=data -o ./data/data.go ./data/... 16 | 17 | const ( 18 | renderWidth = 640 19 | renderHeight = 360 20 | ) 21 | 22 | func main() { 23 | fx := newFx("data/ballet-egon-256.png") 24 | gfx.Run(func() { gfx.RunTimed(fx) }) 25 | } 26 | 27 | type fx struct { 28 | img *image.Paletted 29 | draw *image.Paletted 30 | logW, logH uint 31 | lines [][]byte 32 | } 33 | 34 | func newFx(file string) *fx { 35 | var fx fx 36 | 37 | // Load picture 38 | img, err := gfx.LoadPalPicture(file) 39 | if err != nil { 40 | panic(err) 41 | } 42 | fx.img = img 43 | 44 | // Calculate the with in log(2) 45 | fx.logW = uint(bits.Len32(uint32(img.Rect.Dx()))) - 1 46 | fx.logH = uint(bits.Len32(uint32(img.Rect.Dy()))) - 1 47 | 48 | // Ensure that the width and height are actually powers of two 49 | if img.Rect.Dx() != 1<> DecimalPointLog) & yMask 92 | // Pre-shift, so srcY is offset for x=0 at our line. 93 | srcY <<= fx.logW 94 | for x := range line { 95 | srcX := ((x0 + x*tt) >> DecimalPointLog) & xMask 96 | line[x] = fx.img.Pix[srcX+srcY] 97 | } 98 | } 99 | return fx.draw 100 | } 101 | -------------------------------------------------------------------------------- /ep02/wasm.cmd: -------------------------------------------------------------------------------- 1 | SET GOOS= 2 | SET GOARCH= 3 | 4 | go generate 5 | 6 | SET GOOS=js 7 | SET GOARCH=wasm 8 | go build -o fx.wasm main.go 9 | -------------------------------------------------------------------------------- /ep03/data/ashleymcnamara-pride_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/klauspost/gad/8074eba3799ada5ddcd8a71f794efa03de654fb9/ep03/data/ashleymcnamara-pride_256.png -------------------------------------------------------------------------------- /ep03/data/click.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/klauspost/gad/8074eba3799ada5ddcd8a71f794efa03de654fb9/ep03/data/click.png -------------------------------------------------------------------------------- /ep03/data/inject.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import "github.com/klauspost/gfx" 4 | 5 | func init() { 6 | gfx.AddData(Asset) 7 | } 8 | -------------------------------------------------------------------------------- /ep03/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Episode 3 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | Canvas not supported 14 |
15 |
16 | 17 | 18 | 19 |
Full Screen
20 |
21 | 22 |
Starting
23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /ep03/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "image" 5 | _ "image/png" 6 | "math" 7 | "math/bits" 8 | 9 | _ "github.com/klauspost/gad/ep03/data" // Load data. 10 | "github.com/klauspost/gfx" 11 | ) 12 | 13 | // Generates binary data. 14 | // To install go-bindata, do: go get -u github.com/jteeuwen/go-bindata/... 15 | // 16 | //go:generate go-bindata -ignore=\.go\z -pkg=data -o ./data/data.go ./data/... 17 | 18 | const ( 19 | renderWidth = 640 20 | renderHeight = 360 21 | ) 22 | 23 | func main() { 24 | fx := newRotoZoom("data/ashleymcnamara-pride_256.png") 25 | gfx.Run(func() { gfx.RunTimed(fx) }) 26 | } 27 | 28 | type RotoZoomer struct { 29 | img *image.Paletted 30 | draw *image.Paletted 31 | logW, logH uint 32 | lines [][]byte 33 | } 34 | 35 | func newRotoZoom(file string) *RotoZoomer { 36 | var rz RotoZoomer 37 | 38 | // Load picture 39 | img, err := gfx.LoadPalPicture(file) 40 | if err != nil { 41 | panic(err) 42 | } 43 | rz.img = img 44 | 45 | // Calculate the with in log(2) 46 | rz.logW = uint(bits.Len32(uint32(img.Rect.Dx()))) - 1 47 | rz.logH = uint(bits.Len32(uint32(img.Rect.Dy()))) - 1 48 | 49 | // Ensure that the width and height are actually powers of two 50 | if img.Rect.Dx() != 1<> DecimalPointLog) & uMask 105 | srcY := (v >> DecimalPointLog) & vMask 106 | line[x] = rz.img.Pix[srcX+(srcY<> DecimalPointLog) & uMask 156 | srcY := (v >> DecimalPointLog) & vMask 157 | tex[srcX+(srcY< 2 | 3 | 4 | 5 | Episode 4 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | Canvas not supported 14 |
15 |
16 | 17 | 18 | 19 |
Full Screen
20 |
21 | 22 |
Starting
23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /ep04/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "image" 5 | _ "image/png" 6 | "math" 7 | "math/bits" 8 | 9 | _ "github.com/klauspost/gad/ep04/data" // Load data. 10 | "github.com/klauspost/gfx" 11 | ) 12 | 13 | const ( 14 | renderWidth = 640 15 | renderHeight = 360 16 | ) 17 | 18 | // Generates binary data. 19 | // To install go-bindata, do: go get -u github.com/jteeuwen/go-bindata/... 20 | // 21 | //go:generate go-bindata -ignore=\.go\z -pkg=data -o ./data/data.go ./data/... 22 | 23 | func main() { 24 | fx := newTunnel("data/wildtextures-african-inspir.png") 25 | //gfx.RunWriteToDisk(fx, 1, "./saved/tunnel-%05d.png") 26 | gfx.Run(func() { gfx.RunTimed(fx) }) 27 | } 28 | 29 | type tunnel struct { 30 | img *image.Paletted 31 | draw *image.Paletted 32 | logW, logH uint 33 | lines [][]byte 34 | lookup [][]uint32 35 | } 36 | 37 | func newTunnel(file string) *tunnel { 38 | var fx tunnel 39 | 40 | // Load picture 41 | img, err := gfx.LoadPalPicture(file) 42 | if err != nil { 43 | panic(err) 44 | } 45 | fx.img = img 46 | 47 | // Calculate the with in log(2) 48 | fx.logW = uint(bits.Len32(uint32(img.Rect.Dx()))) - 1 49 | fx.logH = uint(bits.Len32(uint32(img.Rect.Dy()))) - 1 50 | 51 | // Ensure that the width and height are actually powers of two 52 | if img.Rect.Dx() != 1< coordPrec 77 | distance := int(ratio*coordPrec/math.Sqrt(centerX*centerX+centerY*centerY)) % coordPrec 78 | // angle is v coordinate 0 -> coordPrec 79 | angle := (int)(0.5*coordPrec*math.Atan2(centerY, centerX)/math.Pi) % coordPrec 80 | 81 | // Store distance as lower 16 bits, angle as upper 82 | lu[x] = uint32(distance) | uint32(angle<<16) 83 | } 84 | fx.lookup[y] = lu 85 | } 86 | return &fx 87 | } 88 | 89 | // Number of bits to represent width/height of texture. 90 | const coordBits = 16 91 | const coordPrec = 1 << coordBits 92 | 93 | // Render the effect at time t. 94 | func (fx *tunnel) Render(t float64) image.Image { 95 | // fixed point precision 96 | fpX, fpY := coordBits-fx.logW, coordBits-fx.logH 97 | dmX, dmY := 1<> fpX) & uMask 111 | v := ((offsetV + (pos >> 16)) >> fpY) & vMask 112 | line[x] = fx.img.Pix[u+(v< 2 | 3 | 4 | 5 | Episode 5 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | Canvas not supported 14 |
15 |
16 | 17 | 18 | 19 |
Full Screen
20 |
21 | 22 |
Starting
23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /ep05/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "image" 5 | "image/color" 6 | _ "image/png" 7 | "math" 8 | "math/rand" 9 | "time" 10 | 11 | _ "github.com/klauspost/gad/ep05/data" // Load data. 12 | "github.com/klauspost/gfx" 13 | ) 14 | 15 | const ( 16 | renderWidth = 640 17 | renderHeight = 360 18 | ) 19 | 20 | // Generates binary data. 21 | // To install go-bindata, do: go get -u github.com/jteeuwen/go-bindata/... 22 | // 23 | //go:generate go-bindata -ignore=\.go\z -pkg=data -o ./data/data.go ./data/... 24 | 25 | func main() { 26 | gfx.InitShadedPalette(192, color.RGBA{R: 251, G: 246, B: 158}) 27 | fx := newFx() 28 | //gfx.RunWriteToDisk(fx, 1, "./saved/dots-%05d.png") 29 | gfx.Run(func() { gfx.RunTimed(fx) }) 30 | } 31 | 32 | type coord struct{ x, y, z float32 } 33 | 34 | type fx struct { 35 | draw *image.Gray 36 | logW, logH uint 37 | lines [][]byte 38 | dots []coord 39 | } 40 | 41 | func newFx() *fx { 42 | var fx fx 43 | 44 | // Create our draw buffer 45 | fx.draw = image.NewGray(image.Rect(0, 0, renderWidth, renderHeight)) 46 | 47 | // Store each line as a slice in a slice. 48 | w, h := fx.draw.Rect.Dx(), fx.draw.Rect.Dy() 49 | fx.lines = make([][]byte, h) 50 | for y := range fx.lines { 51 | fx.lines[y] = fx.draw.Pix[y*fx.draw.Stride : y*fx.draw.Stride+w] 52 | } 53 | 54 | // Generate some dots in a cylinder along positive z axis. 55 | const dots = 50000 56 | fx.dots = make([]coord, dots) 57 | rng := rand.New(rand.NewSource(time.Now().UnixNano())) 58 | for i := range fx.dots { 59 | // Random angle 60 | angle := rng.Float64() * math.Pi * 2 61 | // Random radius. 62 | r := rng.Float64() * renderWidth * 3 63 | // Random depth 64 | z := rng.Float32() * 50 65 | x := math.Sin(angle) 66 | y := math.Cos(angle) 67 | 68 | fx.dots[i] = coord{x: float32(x * r), y: float32(y * r), z: z} 69 | } 70 | return &fx 71 | } 72 | 73 | // Render the effect at time t. 74 | func (fx *fx) Render(t float64) image.Image { 75 | // Clear the screen or keep trails 76 | for i := range fx.draw.Pix { 77 | //fx.draw.Pix[i] = 0 78 | fx.draw.Pix[i] = fx.draw.Pix[i] >> 1 79 | } 80 | 81 | angleSin, angleCos := float32(1), float32(0) 82 | if true { 83 | // Rotate 0 -> 180 degrees when t goes 0 -> 1 84 | angleSin = float32(math.Sin(t * math.Pi)) 85 | angleCos = float32(math.Cos(t * math.Pi)) 86 | } 87 | 88 | // t2 goes from 0 -> 2 -> 0 89 | t2 := float32(math.Sin(t*math.Pi)) * 2 90 | // Offset z on all points over time, effectively moving the "camera" forward. 91 | zoff := 3 - 5*t2 92 | 93 | const ( 94 | // We calculate output contribution using the z depth. 95 | // We use zMaxValue and subtract (z * zFalloff) for each dot. 96 | zMaxValue = 200 97 | zFalloff = 20.0 98 | // maxZ is where the contribution becomes zero. 99 | // zMaxValue - zFalloff*z = 0 100 | maxZ = zMaxValue / zFalloff 101 | ) 102 | 103 | // Draw all our dots 104 | for _, d := range fx.dots { 105 | z := d.z + zoff 106 | if z <= 0 || z >= maxZ { 107 | // behind the camera or no contribution, skip 108 | continue 109 | } 110 | // Scale x+y and perspective project 111 | x := (t2 * d.x) / z 112 | y := (t2 * d.y) / z 113 | 114 | // 2d rotate while still centered on 0,0 115 | x, y = x*angleCos-y*angleSin, x*angleSin+y*angleCos 116 | 117 | x += renderWidth / 2 118 | y += renderHeight / 2 119 | 120 | if y := int(y); y >= 0 && y < renderHeight { 121 | x := int(x) 122 | if x >= 0 && x < renderWidth { 123 | l := fx.lines[y] 124 | l[x] = clamp8(int(l[x]) + (zMaxValue - int(z*zFalloff))) 125 | } 126 | } 127 | } 128 | return fx.draw 129 | } 130 | 131 | func clamp8(v int) uint8 { 132 | if v >= 255 { 133 | return 255 134 | } 135 | if v <= 0 { 136 | return 0 137 | } 138 | return uint8(v) 139 | } 140 | -------------------------------------------------------------------------------- /ep05/wasm.cmd: -------------------------------------------------------------------------------- 1 | SET GOOS= 2 | SET GOARCH= 3 | 4 | go generate 5 | 6 | SET GOOS=js 7 | SET GOARCH=wasm 8 | go build -o fx.wasm main.go 9 | -------------------------------------------------------------------------------- /ep06/data/click.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/klauspost/gad/8074eba3799ada5ddcd8a71f794efa03de654fb9/ep06/data/click.png -------------------------------------------------------------------------------- /ep06/data/flower.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/klauspost/gad/8074eba3799ada5ddcd8a71f794efa03de654fb9/ep06/data/flower.png -------------------------------------------------------------------------------- /ep06/data/inject.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import "github.com/klauspost/gfx" 4 | 5 | func init() { 6 | gfx.AddData(Asset) 7 | } 8 | -------------------------------------------------------------------------------- /ep06/data/snowflake1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/klauspost/gad/8074eba3799ada5ddcd8a71f794efa03de654fb9/ep06/data/snowflake1.png -------------------------------------------------------------------------------- /ep06/data/snowflake2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/klauspost/gad/8074eba3799ada5ddcd8a71f794efa03de654fb9/ep06/data/snowflake2.png -------------------------------------------------------------------------------- /ep06/data/snowflake3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/klauspost/gad/8074eba3799ada5ddcd8a71f794efa03de654fb9/ep06/data/snowflake3.png -------------------------------------------------------------------------------- /ep06/data/snowflake4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/klauspost/gad/8074eba3799ada5ddcd8a71f794efa03de654fb9/ep06/data/snowflake4.png -------------------------------------------------------------------------------- /ep06/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Episode 6 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | Canvas not supported 14 |
15 |
16 | 17 | 18 | 19 |
Full Screen
20 |
21 | 22 |
Starting
23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /ep06/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "image" 6 | "image/color" 7 | _ "image/png" 8 | "math" 9 | "math/bits" 10 | "math/rand" 11 | 12 | _ "github.com/klauspost/gad/ep06/data" // Load data. 13 | "github.com/klauspost/gfx" 14 | "golang.org/x/image/draw" 15 | ) 16 | 17 | const ( 18 | renderWidth = 640 19 | renderHeight = 360 20 | ) 21 | 22 | // Generates binary data. 23 | // To install go-bindata, do: go get -u github.com/jteeuwen/go-bindata/... 24 | // 25 | //go:generate go-bindata -ignore=\.go\z -pkg=data -o ./data/data.go ./data/... 26 | 27 | func main() { 28 | //rz := newFx("./data/pride_circle_grey.png") 29 | //rz := newFx("./data/light.png") 30 | gfx.InitShadedPalette(180, color.RGBA{R: 110, G: 110, B: 200}) 31 | 32 | //fx := newFx("data/flower.png") 33 | fx := newFx("data/snowflake2.png") 34 | gfx.Run(func() { gfx.RunTimed(fx) }) 35 | //gfx.RunWriteToDisk(fx, 1, "./saved/snow-%05d.png") 36 | } 37 | 38 | type vec2 struct{ u, v float32 } 39 | type vec3 struct{ x, y, z float32 } 40 | 41 | // clipWrap will clip by wrapping, asteroid style. 42 | func (v vec2) clipWrap(n float32) float32 { 43 | if n >= v.u && n <= v.v { 44 | return n 45 | } 46 | return v.u + float32(math.Mod(float64(n-v.u), float64(v.v-v.u))) 47 | } 48 | 49 | type particle struct { 50 | basePos vec3 51 | speed vec3 52 | } 53 | 54 | type scene struct { 55 | parts []particle 56 | projected []vec3 57 | bounds struct { 58 | y vec2 59 | } 60 | } 61 | 62 | func (s *scene) generate() { 63 | const parts = 2500 64 | s.parts = make([]particle, parts) 65 | rng := rand.New(rand.NewSource(0xc0cac01a)) 66 | const depth = 5 67 | for i := range s.parts { 68 | part := &s.parts[i] 69 | part.basePos = vec3{ 70 | x: -(renderWidth * depth) + rng.Float32()*renderWidth*depth*2, 71 | y: -(renderHeight * depth) + rng.Float32()*renderHeight*depth*2, 72 | z: 0.1 + rng.Float32()*4*depth, 73 | } 74 | part.speed = vec3{ 75 | x: 10 + rng.Float32()*50, 76 | y: 100 + rng.Float32()*500, 77 | z: rng.Float32(), 78 | } 79 | } 80 | s.projected = make([]vec3, parts) 81 | s.bounds.y = vec2{-renderHeight * depth * 1.5, renderHeight * depth * 1.5} 82 | } 83 | 84 | func (s scene) At(t float32) { 85 | dst := s.projected 86 | if len(s.parts) != len(dst) { 87 | panic(fmt.Sprintf("len(s.parts) != len(dst), %d != %d", len(s.parts), len(dst))) 88 | } 89 | for i, p := range s.parts { 90 | var pos vec3 91 | pos.x = p.basePos.x + p.speed.x*float32(math.Sin(float64(p.speed.x+p.speed.z*t*math.Pi*4))) 92 | pos.y = s.bounds.y.clipWrap(p.basePos.y + (p.speed.y * t)) 93 | pos.z = max32(0.1, p.basePos.z+p.speed.z*0.5*float32(math.Sin(float64(p.speed.z*50+p.speed.z*t*math.Pi)))) 94 | 95 | d := &dst[i] 96 | invZ := 1.0 / pos.z 97 | d.x = pos.x*invZ + renderWidth/2 98 | d.y = pos.y*invZ + renderHeight/2 99 | d.z = invZ 100 | } 101 | } 102 | 103 | type fx struct { 104 | // main and mipmaps 105 | img *image.Gray 106 | mipmaps []*image.Gray 107 | draw *image.Gray 108 | logW, logH uint 109 | lines [][]byte 110 | scene scene 111 | } 112 | 113 | func newFx(file string) *fx { 114 | var fx fx 115 | 116 | // Load picture 117 | img, err := gfx.LoadPalPicture(file) 118 | if err != nil { 119 | panic(err) 120 | } 121 | // Ensure we are grayscale. 122 | grey := image.NewGray(img.Rect) 123 | draw.Draw(grey, grey.Rect, img, image.Pt(0, 0), draw.Src) 124 | fx.img = grey 125 | 126 | // Calculate the with in log(2) 127 | fx.logW = uint(bits.Len32(uint32(img.Rect.Dx()))) - 1 128 | fx.logH = uint(bits.Len32(uint32(img.Rect.Dy()))) - 1 129 | 130 | // Ensure that the width and height are actually powers of two 131 | if img.Rect.Dx() != 1<= 0; i-- { 156 | img := image.NewGray(image.Rect(0, 0, prev.Rect.Dx()/2, prev.Rect.Dy()/2)) 157 | fmt.Println(i, img.Rect) 158 | fx.mipmaps[i] = img 159 | for y := 0; y < img.Rect.Dy(); y++ { 160 | src0, src1 := prev.Pix[y*2*prev.Stride:], prev.Pix[(y*2+1)*prev.Stride:] 161 | dst := img.Pix[y*img.Stride : (y+1)*img.Stride] 162 | for x := range dst { 163 | // Average 4 pixels. 164 | dst[x] = uint8((uint(src0[x*2]) + uint(src0[x*2+1]) + uint(src1[x*2]) + uint(src0[x*2+1]) + 2) >> 2) 165 | } 166 | } 167 | prev = img 168 | } 169 | fx.scene.generate() 170 | return &fx 171 | } 172 | 173 | // Render the effect at time t. 174 | func (fx *fx) Render(t float64) image.Image { 175 | //drawFn := fx.drawSpriteFast 176 | //drawFn := fx.drawSpriteMip 177 | drawFn := fx.drawSpriteNice 178 | //drawFn := fx.drawSpriteGo 179 | 180 | return fx.RenderParticles(t, drawFn) 181 | 182 | for i := range fx.draw.Pix { 183 | fx.draw.Pix[i] = 0 184 | } 185 | // return fx.Render2D(t, drawFn) 186 | return fx.RenderTest(t, drawFn) 187 | } 188 | 189 | // Render the effect at time t. 190 | func (fx *fx) RenderParticles(t float64, drawFn func(x, y, r int32)) image.Image { 191 | // Render fake sky/ground. 192 | for y, line := range fx.lines { 193 | f := 255.0 - 192 194 | if y < 64 { 195 | f = math.Abs(256 - float64(y*2) - 64) 196 | } 197 | if y > renderHeight-96 { 198 | f = math.Abs(128 - renderHeight + float64(y+20)*1.15) 199 | } 200 | v := uint8(math.Min(255, math.Max(0, f))) 201 | for x := range line { 202 | line[x] = v 203 | } 204 | } 205 | 206 | fx.scene.At(float32(t)) 207 | for _, p := range fx.scene.projected { 208 | drawFn(int32(p.x*256), int32(p.y*256), int32(30*256*p.z)-300) 209 | } 210 | 211 | return fx.draw 212 | } 213 | 214 | func (fx *fx) Render2D(t float64, drawFn func(x, y, r int32)) image.Image { 215 | const ( 216 | halfWidth = renderWidth * 0.5 217 | halfHeight = renderHeight * 0.5 218 | ) 219 | 220 | for i := int32(0); i < 1024; i++ { 221 | x := 10 + 20*(i%32-16) 222 | y := 20 * (i/32 - 16) 223 | r := 7 * math.Abs(math.Sin(t+t*float64(x)/5)+math.Cos(t+t*float64(y)/3)) 224 | drawFn(x*256+halfWidth*256, y*256+halfHeight*256, int32(256*r)) 225 | } 226 | return fx.draw 227 | } 228 | 229 | func (fx *fx) RenderTest(t float64, drawFn func(x, y, r int32)) image.Image { 230 | const ( 231 | halfWidth = renderWidth * 0.5 232 | halfHeight = renderHeight * 0.5 233 | ) 234 | drawFn( 235 | int32(halfWidth*256+256*50*math.Sin(t*math.Pi*2*4)), 236 | int32(halfHeight*256+256*50*math.Cos(t*math.Pi*2*4)), 237 | //int32(256*50), 238 | int32(256*130*math.Abs(math.Sin(t*math.Pi))), 239 | ) 240 | drawFn( 241 | int32(150*256+256*t*100), 242 | int32(55*256), 243 | int32(256*50), 244 | ) 245 | drawFn( 246 | int32(55*256), 247 | int32(256*50+256*t*100), 248 | int32(256*50), 249 | ) 250 | 251 | drawFn( 252 | int32(halfWidth*1.5*256+255), 253 | int32(halfHeight*256+255), 254 | int32(256*150*(1-t)), 255 | ) 256 | drawFn( 257 | int32(55*256), 258 | int32(255*256), 259 | int32(256*15*math.Abs(math.Cos(t*math.Pi*10))), 260 | ) 261 | drawFn( 262 | int32(100*256+int32(t*200*256)), 263 | int32(255*256), 264 | int32(256*2*math.Abs(math.Cos(t*math.Pi*2))), 265 | ) 266 | return fx.draw 267 | } 268 | 269 | // drawSpriteFast will draw a particle centered at x,y with radius r. 270 | // The image is drawn with nearest neighbor scaling, but subpixel position and radius. 271 | // Input is assumed to be 24.8 272 | func (fx *fx) drawSpriteFast(x, y, r int32) { 273 | m := fx.calcMapping(x, y, r, int32(len(fx.mipmaps)-1)) 274 | if m.mip == nil { 275 | return 276 | } 277 | 278 | v := m.v0 279 | for y := m.startY; y < m.endY; y++ { 280 | // First destination pixel 281 | dst := fx.draw.Pix[int(y)*fx.draw.Stride:] 282 | // Line in mipmap to read from. 283 | mipLine := m.mip.Pix[(v>>16)*uint32(m.mip.Stride):] 284 | // Reset u 285 | u := m.u0 286 | for x := m.startX; x < m.endX; x++ { 287 | // Read from u 288 | xPos := u >> 16 289 | // Add input to output. 290 | dst[x] = clamp8uint32(uint32(mipLine[xPos]) + uint32(dst[x])) 291 | // Offset u for every pixel 292 | u += m.uStep 293 | } 294 | // Offset v for every line 295 | v += m.vStep 296 | } 297 | } 298 | 299 | // drawSpriteMip will draw a particle centered at x,y with radius r. 300 | // The image is drawn with nearest neighbor scaling, but choosing from a mipmap 301 | // and with subpixel position and radius. 302 | // Input is assumed to be 24.8 303 | func (fx *fx) drawSpriteMip(x, y, r int32) { 304 | m := fx.calcMapping(x, y, r, -1) 305 | if m.mip == nil { 306 | return 307 | } 308 | 309 | // This is similar to drawSpriteFast 310 | v := m.v0 311 | for y := m.startY; y < m.endY; y++ { 312 | dst := fx.draw.Pix[int(y)*fx.draw.Stride:] 313 | mipLine := m.mip.Pix[(v>>16)*uint32(m.mip.Stride):] 314 | u := m.u0 315 | for x := m.startX; x < m.endX; x++ { 316 | xPos := u >> 16 317 | dst[x] = clamp8uint32(uint32(mipLine[xPos]) + uint32(dst[x])) 318 | u += m.uStep 319 | } 320 | v += m.vStep 321 | } 322 | } 323 | 324 | // drawSpriteNice will draw a particle centered at x,y with radius r. 325 | // The input is selected from the appropriate mipmap and bilinear interpolation is used. 326 | // The image position and size have subpixel precision. 327 | // Input is assumed to be 24.8 328 | func (fx *fx) drawSpriteNice(x, y, r int32) { 329 | m := fx.calcMapping(x, y, r, -1) 330 | if m.mip == nil { 331 | return 332 | } 333 | // Mipmap must be at least 2x2. 334 | v := m.v0 335 | for y := m.startY; y < m.endY; y++ { 336 | dst := fx.draw.Pix[int(y)*fx.draw.Stride:] 337 | 338 | // Input line above and below the current desired input. 339 | mipLine0 := m.mip.Pix[(v>>16)*uint32(m.mip.Stride):] 340 | mipLine1 := mipLine0 341 | 342 | // Calculate weight for lines above/below desired input. 343 | vf1 := (v & 0xffff) >> 8 344 | vf0 := 256 - vf1 345 | if (v + 65536) < m.mipSize { 346 | // Set mipline 1 to next line unless last line. 347 | mipLine1 = mipLine0[m.mip.Stride:] 348 | } 349 | u := m.u0 350 | for x := m.startX; x < m.endX; x++ { 351 | // Calculate pixel offset before and after desired pixel. 352 | xPos0 := u >> 16 353 | xPos1 := xPos0 354 | if u+65536 < m.mipSize { 355 | xPos1++ 356 | } 357 | // Calculate weights as fp24.8. 358 | uf1 := (u & 0xffff) >> 8 359 | uf0 := 256 - uf1 360 | // Using the calculated weights, calculate output pixel, scaled up 16 bits. 361 | pix := uint32(mipLine0[xPos0]) * uf0 * vf0 362 | pix += uint32(mipLine0[xPos1]) * uf1 * vf0 363 | pix += uint32(mipLine1[xPos0]) * uf0 * vf1 364 | pix += uint32(mipLine1[xPos1]) * uf1 * vf1 365 | 366 | // Add output to current pixel value. 367 | dst[x] = clamp8uint32((pix >> 16) + uint32(dst[x])) 368 | u += m.uStep 369 | } 370 | v += m.vStep 371 | } 372 | } 373 | 374 | // drawSpriteGo will draw a particle centered at x,y with radius r. 375 | // Input is assumed to be 24.8 376 | func (fx *fx) drawSpriteGo(x, y, r int32) { 377 | m := fx.calcMapping(x, y, r, -1) 378 | if m.startX == m.endX || m.startY == m.endY || m.mip == nil { 379 | return 380 | } 381 | draw.ApproxBiLinear.Scale(fx.draw, image.Rect(int(m.startX), int(m.startY), int(m.endX), int(m.endY)), 382 | image.NewUniform(color.White), image.Rect(int(m.u0>>16), int(m.v0>>16), int(m.u1>>16), int(m.v1>>16)), 383 | draw.Over, &draw.Options{ 384 | SrcMask: grayToShallowAlpha(m.mip), 385 | }) 386 | } 387 | 388 | // mapping contains the information about the sprite needed 389 | // to draw it to the screen without bounds checks. 390 | type mapping struct { 391 | // Start and end coordinates in screen space. 392 | // This is where we will be drawing the pixels. 393 | // This is directly translatable to a screen coordinate. 394 | startX, endX, startY, endY int32 395 | 396 | // Start and end coordinates in 16.16 fixed point coordinates on the source texture. 397 | // One pixel on source is equal to 65536. 398 | u0, u1, v0, v1 uint32 399 | 400 | // Every pixel increment u and v by this when moving one pixel in screen space. 401 | vStep, uStep uint32 402 | 403 | // The size (width/height) of the chosen mip in uv scale. 404 | mipSize uint32 405 | 406 | // The image to draw from. 407 | // If nil, do not draw anything. 408 | mip *image.Gray 409 | } 410 | 411 | // calcMapping will return a mapping for a sprite with radius r placed at (x,y) 412 | // at the specified mip level. 413 | func (fx *fx) calcMapping(x, y, r, mip int32) mapping { 414 | var m mapping 415 | 416 | // Quick discard 417 | if x+r < 0 || x-r > (renderWidth*256) || y+r < 0 || y-r > (renderHeight*256) { 418 | return m 419 | } 420 | 421 | // For very small radius we simply draw a point in a 2x2 square. 422 | if r <= 128 { 423 | m.startX, m.endX = (x-r)>>8, (x-r)>>8+1 424 | m.startY, m.endY = (y-r)>>8, (y-r)>>8+1 425 | if m.startX >= renderWidth || m.startX < 0 || m.startY >= renderHeight || m.startY < 0 { 426 | return m 427 | } 428 | m.u1 = uint32(x-r) & 0xff 429 | m.u0 = 256 - m.u1 430 | m.v1 = uint32(y-r) & 0xff 431 | m.v0 = 256 - m.v1 432 | 433 | // Radius times 1x1 pixel value. 434 | rmip := (r * int32(fx.mipmaps[0].Pix[0])) >> 5 435 | m.u0 = (m.u0 * uint32(rmip)) >> 8 436 | m.u1 = (m.u1 * uint32(rmip)) >> 8 437 | m.v0 = (m.v0 * uint32(rmip)) >> 8 438 | m.v1 = (m.v1 * uint32(rmip)) >> 8 439 | m.mipSize = 1 440 | // leave mip nil 441 | fx.drawPoint(&m) 442 | return m 443 | } 444 | 445 | mipLevel := mip 446 | if mip < 0 { 447 | mipLevel = int32(bits.Len32(uint32(r>>6))) - 1 448 | if int(mipLevel) >= len(fx.mipmaps) { 449 | mipLevel = int32(len(fx.mipmaps)) - 1 450 | } else if mipLevel < 1 { 451 | mipLevel = 1 452 | } 453 | } 454 | m.mip = fx.mipmaps[mipLevel] 455 | m.mipSize = uint32(1<<16) << uint(mipLevel) 456 | 457 | // output pixels per texture pixels * 256 458 | textureScale := float64(m.mip.Rect.Dx()) / (float64(r) / (128 * 256)) 459 | 460 | // Screen space start, rounded down 461 | m.startX, m.startY = (x-r)>>8, (y-r)>>8 462 | // Screen space, rounded up. 463 | m.endX, m.endY = (x+r+255)>>8, (y+r+255)>>8 464 | 465 | // Calculate rounded difference and convert to texture space. 466 | m.u0, m.v0 = 255-uint32((x-r)-(m.startX<<8)), 255-uint32((y-r)-(m.startY<<8)) 467 | m.u0, m.v0 = uint32(float64(m.u0)*textureScale), uint32(float64(m.v0)*textureScale) 468 | 469 | // Calculate rounded difference and convert to texture space. 470 | m.u1, m.v1 = 255-uint32((m.endX<<8)-(x+r)), 255-uint32((m.endY<<8)-(y+r)) 471 | m.u1, m.v1 = m.mipSize-uint32(float64(m.u1)*textureScale), m.mipSize-uint32(float64(m.v1)*textureScale) 472 | 473 | // Calculate step size per screen space pixel. 474 | m.uStep = uint32(float64(m.u1-m.u0) / float64(m.endX-m.startX)) 475 | m.vStep = uint32(float64(m.v1-m.v0) / float64(m.endY-m.startY)) 476 | 477 | if false && (m.uStep == 0 || m.vStep == 0 || m.u0 >= m.u1 || m.v0 >= m.v1) { 478 | fmt.Printf("r:%d, m:%+v v0:%v, v1: %v scale:%v (%d, %d), x,y (%v, %v)\n", 479 | r, m, 256-uint32((y-r)-(m.startY<<8)), 256-uint32((m.endY<<8)-(y+r)), 480 | textureScale/256, m.endX-m.startX, m.mip.Rect.Dx(), 481 | float64(x)/256, float64(y)/256) 482 | // Sanity check 483 | m.mip = nil 484 | return m 485 | } 486 | 487 | // Clip left, top, right, bottom. 488 | if m.startX < 0 { 489 | m.u0 += m.uStep * uint32(-m.startX) 490 | m.startX = 0 491 | } 492 | if m.startY < 0 { 493 | m.v0 += m.vStep * uint32(-m.startY) 494 | m.startY = 0 495 | } 496 | if m.endX > renderWidth { 497 | // Not needed for most 498 | m.v1 -= uint32(m.endX-renderWidth) * m.uStep 499 | m.endX = renderWidth 500 | } 501 | if m.endY > renderHeight { 502 | // Not needed for most 503 | m.v1 -= uint32(m.endY-renderHeight) * m.vStep 504 | m.endY = renderHeight 505 | } 506 | 507 | if false { 508 | // Final sanity to make sure we don't go over due to rounding. 509 | for ; m.u0+m.uStep*uint32(m.endX-m.startX) >= m.mipSize; m.endX-- { 510 | } 511 | for ; m.v0+m.vStep*uint32(m.endY-m.startY) >= m.mipSize; m.endY-- { 512 | } 513 | } 514 | return m 515 | } 516 | 517 | // drawPoint will draw a single point between the pixel. 518 | func (fx *fx) drawPoint(m *mapping) { 519 | dst := fx.draw.Pix[m.startX+m.startY*int32(fx.draw.Stride):] 520 | dst[0] = clamp8uint32((m.u0*m.v0)>>8 + uint32(dst[0])) 521 | if m.endX < renderWidth { 522 | dst[1] = clamp8uint32((m.u1*m.v0)>>8 + uint32(dst[1])) 523 | } 524 | if m.endY >= renderHeight { 525 | return 526 | } 527 | dst = dst[fx.draw.Stride:] 528 | dst[0] = clamp8uint32((m.u0*m.v1)>>8 + uint32(dst[0])) 529 | if m.endX < renderWidth { 530 | dst[1] = clamp8uint32((m.u1*m.v1)>>8 + uint32(dst[1])) 531 | } 532 | } 533 | 534 | func clamp8uint32(v uint32) uint8 { 535 | if v >= 255 { 536 | return 255 537 | } 538 | return uint8(v) 539 | } 540 | 541 | // grayToShallowAlpha converts the grey image data to an alpha image. 542 | // The image data is a shallow (non-copy) representation of the input pixels. 543 | func grayToShallowAlpha(src *image.Gray) *image.Alpha { 544 | return &image.Alpha{ 545 | Pix: src.Pix, 546 | Stride: src.Stride, 547 | Rect: src.Rect, 548 | } 549 | } 550 | 551 | func max32(a, b float32) float32 { 552 | if a >= b { 553 | return a 554 | } 555 | return b 556 | } 557 | 558 | type fp8x24 uint32 559 | type fp16x16 uint32 560 | 561 | func (f fp8x24) String() string { 562 | return fmt.Sprintf("(%d,0x%x)", uint32(f)>>8, uint8(f)) 563 | } 564 | func (f fp16x16) String() string { 565 | return fmt.Sprintf("(%d,0x%x)", uint32(f)>>16, uint16(f)) 566 | } 567 | -------------------------------------------------------------------------------- /ep06/wasm.cmd: -------------------------------------------------------------------------------- 1 | SET GOOS= 2 | SET GOARCH= 3 | 4 | go generate 5 | 6 | SET GOOS=js 7 | SET GOARCH=wasm 8 | go build -o fx.wasm main.go 9 | -------------------------------------------------------------------------------- /ep08/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Episode 8 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 | Canvas not supported 16 |
17 |
18 | 19 | 20 | 21 |
Full Screen
22 |
23 | 24 |
Starting
25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /ep08/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "image" 5 | "time" 6 | 7 | "github.com/klauspost/gad/hoaxplus/00-intro" 8 | "github.com/klauspost/gad/hoaxplus/01-title" 9 | _ "github.com/klauspost/gad/hoaxplus/data" 10 | "github.com/klauspost/gfx" 11 | ) 12 | 13 | var ( 14 | renderWidth = 624 15 | renderHeight = 240 16 | ) 17 | 18 | // Generates binary data. 19 | // To install go-bindata, do: go get -u github.com/jteeuwen/go-bindata/... 20 | // 21 | //go:generate go-bindata -ignore=\.go\z -pkg=data -o ../../data/data.go --prefix=../../data ../../data/... 22 | 23 | func main() { 24 | // Create our draw buffer 25 | screen := image.NewGray(image.Rect(0, 0, renderWidth, renderHeight)) 26 | fullColor := image.NewRGBA(image.Rect(0, 0, renderWidth, renderHeight)) 27 | gfx.SetRenderSize(renderWidth, renderHeight) 28 | gfx.Fullscreen(false) 29 | 30 | fx := intro.NewIntro(screen) 31 | if true { 32 | fx = title.NewTitle(screen, fullColor) 33 | } 34 | gfx.Run(func() { gfx.RunTimedDur(fx, 5*time.Second) }) 35 | } 36 | -------------------------------------------------------------------------------- /ep08/wasm.cmd: -------------------------------------------------------------------------------- 1 | SET GOOS= 2 | SET GOARCH= 3 | 4 | REM go generate 5 | 6 | SET GOOS=js 7 | SET GOARCH=wasm 8 | go build -o fx.wasm main.go 9 | -------------------------------------------------------------------------------- /hoaxplus/00-intro/intro.go: -------------------------------------------------------------------------------- 1 | package intro 2 | 3 | import ( 4 | "github.com/klauspost/gfx" 5 | "image" 6 | ) 7 | 8 | func NewIntro(screen *image.Gray) gfx.TimedEffect { 9 | return &intro{draw: screen} 10 | } 11 | 12 | type intro struct { 13 | draw *image.Gray 14 | cleared bool 15 | } 16 | 17 | func (fx *intro) Render(t float64) image.Image { 18 | if !fx.cleared { 19 | for i := range fx.draw.Pix { 20 | fx.draw.Pix[i] = 0 21 | } 22 | fx.cleared = true 23 | } 24 | img := fx.draw 25 | w, h := img.Rect.Dx(), img.Rect.Dy() 26 | top := img.Pix[:img.Stride] 27 | bottom := img.Pix[(h-1)*img.Stride:] 28 | 29 | xwhere := (int)(float64(w+1) * t) 30 | const white = 255 31 | const grey = 40 32 | 33 | drawLine := func(pix []byte, col byte, start, stop int) { 34 | if start < 0 { 35 | start = 0 36 | } 37 | if stop > len(pix) { 38 | stop = len(pix) 39 | } 40 | for i := start; i < stop; i++ { 41 | pix[i] = col 42 | } 43 | } 44 | drawLine(top, white, 0, xwhere+4) 45 | drawLine(top, grey, 0, xwhere) 46 | drawLine(bottom, white, w-xwhere-4, w) 47 | drawLine(bottom, grey, w-xwhere, w) 48 | return img 49 | } 50 | -------------------------------------------------------------------------------- /hoaxplus/01-title/title.go: -------------------------------------------------------------------------------- 1 | package title 2 | 3 | import ( 4 | "image" 5 | "image/color" 6 | "math" 7 | 8 | "github.com/klauspost/gad/hoaxplus/primitive" 9 | "github.com/klauspost/gfx" 10 | ) 11 | 12 | func NewTitle(draw *image.Gray, screen *image.RGBA) gfx.TimedEffect { 13 | t := title{draw: draw, screen: screen} 14 | for i := range t.color[0][:] { 15 | if i <= 192 { 16 | t.color[0][i] = 17 | color.RGBA{ 18 | R: uint8((248 * i) >> 8), 19 | G: uint8((289 * i) >> 8), 20 | B: uint8((294 * i) >> 8), 21 | A: 0xff, 22 | } 23 | t.color[1][i] = 24 | color.RGBA{ 25 | R: uint8((300 * i) >> 8), 26 | G: uint8((278 * i) >> 8), 27 | B: uint8((252 * i) >> 8), 28 | A: 0xff, 29 | } 30 | } else { 31 | pixrem := i - 192 32 | t.color[0][i] = 33 | color.RGBA{ 34 | R: 198 + uint8((57*pixrem)>>6), 35 | G: 218 + uint8((37*pixrem)>>6), 36 | B: 222 + uint8((33*pixrem)>>6), 37 | A: 0xff, 38 | } 39 | t.color[1][i] = 40 | color.RGBA{ 41 | R: 226 + uint8((29*pixrem)>>6), 42 | G: 210 + uint8((45*pixrem)>>6), 43 | B: 190 + uint8((65*pixrem)>>6), 44 | A: 0xff, 45 | } 46 | } 47 | } 48 | b, err := gfx.Load("Basehead.obj") 49 | if err != nil { 50 | panic(err) 51 | } 52 | t.verts, t.edges = primitive.LoadOBJ(b) 53 | t.vTransformed = make(primitive.P3Ds, len(t.verts)) 54 | t.vProjected = make(primitive.P2Ds, len(t.verts)) 55 | return &t 56 | } 57 | 58 | type title struct { 59 | draw *image.Gray 60 | screen *image.RGBA 61 | cleared bool 62 | verts primitive.P3Ds 63 | edges [][2]int 64 | vTransformed primitive.P3Ds 65 | vProjected primitive.P2Ds 66 | color [2][256]color.RGBA 67 | } 68 | 69 | func (fx *title) Render(t float64) image.Image { 70 | img := fx.draw 71 | for i := range img.Pix { 72 | img.Pix[i] = 192 73 | } 74 | // Convert to float 75 | fw, fh := float32(img.Rect.Dx()), float32(img.Rect.Dy()) 76 | 77 | // Draw line across screen 78 | primitive.Line{P2: primitive.Point2D{X: fw, Y: fh}}.DrawAA(img, 255) 79 | 80 | // Draw model 81 | fx.verts.RotateTo(fx.vTransformed, math.Pi/2, -t*math.Pi*2, 0) 82 | fx.vTransformed.ProjectTo(fx.vProjected, fw, fh, float32(math.Sin(t*math.Pi))*10) 83 | for _, edge := range fx.edges { 84 | p0, p1 := fx.vProjected[edge[0]], fx.vProjected[edge[1]] 85 | if p0.X == primitive.BehindCamera || p1.X == primitive.BehindCamera { 86 | continue 87 | } 88 | primitive.Line{ 89 | P1: p0, 90 | P2: p1, 91 | }.DrawAA(img, 0) 92 | } 93 | 94 | fx.transfer() 95 | return fx.screen 96 | } 97 | 98 | func (fx *title) transfer() { 99 | w, h := fx.screen.Bounds().Dx(), fx.screen.Bounds().Dy() 100 | src := fx.draw 101 | dst := fx.screen 102 | for y := 0; y < h; y++ { 103 | xch := (int)((float64)(y*w) / (float64)(h)) 104 | line := src.Pix[y*src.Stride : y*src.Stride+src.Rect.Dx()] 105 | dLine := dst.Pix[y*dst.Stride : y*dst.Stride+len(line)*4] 106 | 107 | // Left of change 108 | pal := fx.color[0][:] 109 | for x, v := range line[:xch] { 110 | col := pal[v] 111 | dLine[x*4] = col.R 112 | dLine[x*4+1] = col.G 113 | dLine[x*4+2] = col.B 114 | dLine[x*4+3] = 255 115 | } 116 | 117 | // Right side. 118 | line = line[xch:] 119 | dLine = dLine[xch*4:] 120 | pal = fx.color[1][:] 121 | for x, v := range line { 122 | col := pal[v] 123 | dLine[x*4] = col.R 124 | dLine[x*4+1] = col.G 125 | dLine[x*4+2] = col.B 126 | dLine[x*4+3] = 255 127 | } 128 | } 129 | } 130 | 131 | func (fx *title) transferGrey() { 132 | _, h := fx.screen.Bounds().Dx(), fx.screen.Bounds().Dy() 133 | src := fx.draw 134 | dst := fx.screen 135 | for y := 0; y < h; y++ { 136 | line := src.Pix[y*src.Stride : y*src.Stride+src.Rect.Dx()] 137 | dLine := dst.Pix[y*dst.Stride : y*dst.Stride+len(line)*4] 138 | 139 | for x, v := range line { 140 | dLine[x*4] = v 141 | dLine[x*4+1] = v 142 | dLine[x*4+2] = v 143 | dLine[x*4+3] = 255 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /hoaxplus/cmd/hoax/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "image" 5 | "time" 6 | 7 | "github.com/klauspost/gad/hoaxplus/00-intro" 8 | "github.com/klauspost/gad/hoaxplus/01-title" 9 | _ "github.com/klauspost/gad/hoaxplus/data" 10 | "github.com/klauspost/gfx" 11 | ) 12 | 13 | var ( 14 | renderWidth = 624 15 | renderHeight = 240 16 | ) 17 | 18 | // Generates binary data. 19 | // To install go-bindata, do: go get -u github.com/jteeuwen/go-bindata/... 20 | // 21 | //go:generate go-bindata -ignore=\.go\z -pkg=data -o ../../data/data.go --prefix=../../data ../../data/... 22 | 23 | func main() { 24 | // Create our draw buffer 25 | screen := image.NewGray(image.Rect(0, 0, renderWidth, renderHeight)) 26 | fullColor := image.NewRGBA(image.Rect(0, 0, renderWidth, renderHeight)) 27 | gfx.SetRenderSize(renderWidth, renderHeight) 28 | gfx.Fullscreen(false) 29 | 30 | fx := intro.NewIntro(screen) 31 | if true { 32 | fx = title.NewTitle(screen, fullColor) 33 | } 34 | gfx.Run(func() { gfx.RunTimedDur(fx, 5*time.Second) }) 35 | } 36 | -------------------------------------------------------------------------------- /hoaxplus/data/Basehead.obj: -------------------------------------------------------------------------------- 1 | # Blender v2.69 (sub 0) OBJ File: 'Basehead.blend' 2 | # www.blender.org 3 | # Made by BlenderGuyz https://www.turbosquid.com/FullPreview/Index.cfm/ID/805661 4 | mtllib Basehead.mtl 5 | o Head_Cube 6 | v 0.794880 -0.456218 -1.453191 7 | v 0.631179 -0.544477 1.149647 8 | v 1.138971 -0.447948 -0.797869 9 | v 1.094424 -0.450260 0.416698 10 | v 0.439387 -0.454233 -1.604853 11 | v 0.277122 -0.541346 1.593228 12 | v 0.962543 -0.496748 0.802866 13 | v 1.031688 -0.430602 -1.168014 14 | v 0.707379 -2.084441 -1.645555 15 | v 0.839524 -2.829275 -0.206489 16 | v 1.397615 -2.236477 -0.621362 17 | v 0.434208 -2.923688 -0.123380 18 | v 1.050709 -1.962676 -1.461045 19 | v 1.293451 -2.041516 -1.087568 20 | v 1.235680 -2.613284 -0.304228 21 | v 0.340290 -2.143095 -1.758874 22 | v 1.115928 -2.194080 -1.851622 23 | v 1.845940 -2.157743 -1.262549 24 | v 0.617709 -2.264206 -1.954206 25 | v 2.010724 -2.349208 -0.716563 26 | v 1.200113 -3.008740 -0.212898 27 | v 1.574329 -2.080434 -1.682444 28 | v 0.570588 -3.111907 -0.052411 29 | v 1.740340 -2.762577 -0.342175 30 | v 0.544184 -1.218010 -1.316640 31 | v 0.785863 0.123222 -1.493973 32 | v 0.631784 0.065619 1.101769 33 | v 0.544759 -1.373754 1.087082 34 | v 1.175014 0.129346 -0.859118 35 | v 0.891132 -1.348171 0.039141 36 | v 0.868947 -1.290124 -0.704046 37 | v 1.139899 0.081269 0.384301 38 | v 0.837687 -1.364774 0.456978 39 | v 0.793231 1.461336 -0.051452 40 | v 0.713654 1.338543 -0.865439 41 | v 0.726249 1.374125 0.384978 42 | v 0.409165 -1.889288 0.476818 43 | v 0.413912 -1.767348 0.077705 44 | v 0.259894 -1.211585 -1.410389 45 | v 0.447793 0.127746 -1.647718 46 | v 0.713996 -0.856596 -1.347879 47 | v 0.752636 0.673336 -1.427497 48 | v 0.212867 0.086756 1.329009 49 | v 0.357345 -1.381319 1.188635 50 | v 0.732227 0.650184 1.043815 51 | v 0.654066 -0.939296 1.125531 52 | v 0.889012 -1.449899 -0.343519 53 | v 1.160249 0.646703 -0.086923 54 | v 1.130602 0.601183 -0.831471 55 | v 1.090904 0.593201 0.273403 56 | v 1.075487 -0.935149 -0.039751 57 | v 1.082869 -0.843542 -0.740058 58 | v 1.020563 -0.873545 0.347217 59 | v 0.772650 1.426925 -0.451773 60 | v 0.482622 1.605282 -0.040162 61 | v 0.426543 1.448078 -0.917558 62 | v 0.443451 1.503576 0.417306 63 | v 0.184642 -1.945136 0.517415 64 | v 0.176943 -1.828176 0.063050 65 | v 0.751049 -1.803618 -1.319485 66 | v 0.268966 -2.611370 -0.157706 67 | v 0.392801 -1.919171 -0.138765 68 | v 0.684180 1.119959 0.843505 69 | v 0.639849 1.116517 -1.197492 70 | v 1.045719 1.150136 -0.052040 71 | v 0.960438 1.120901 -0.785369 72 | v 0.975766 0.100961 0.771586 73 | v 0.742840 -1.359286 0.823574 74 | v 0.379030 -1.751386 0.972500 75 | v 1.068913 0.117326 -1.234360 76 | v 0.748300 -1.229600 -1.130803 77 | v 0.958901 -2.034204 -0.563438 78 | v 0.910357 -1.874164 -0.970900 79 | v 0.685983 -1.642155 0.064883 80 | v 0.545493 -2.517920 -0.209344 81 | v 0.988320 1.116698 0.346230 82 | v 0.810883 -2.329444 -0.291012 83 | v 0.540159 -1.931201 -1.516239 84 | v 0.648070 -1.672735 0.417637 85 | v 0.394747 -0.858695 -1.479472 86 | v 0.428887 0.688365 -1.571638 87 | v 0.281567 0.656137 1.176196 88 | v 0.313651 -0.949308 1.240532 89 | v 1.172400 0.599202 -0.417715 90 | v 1.093921 -0.881204 -0.391642 91 | v 0.470959 1.561494 -0.484958 92 | v 0.183718 -1.955385 -0.116337 93 | v 0.364051 1.225281 0.905220 94 | v 0.365010 1.202835 -1.287747 95 | v 1.028806 1.145044 -0.410251 96 | v 0.970931 0.601422 0.706430 97 | v 0.895122 -0.912495 0.819637 98 | v 0.219718 -1.781411 1.040744 99 | v 1.020569 0.612032 -1.172499 100 | v 0.978438 -0.840622 -1.122864 101 | v 0.275865 -1.996229 -1.638517 102 | v 0.621035 -1.760691 -0.169148 103 | v 0.913952 0.981099 0.682378 104 | v 0.898184 0.993185 -1.079507 105 | v 0.576067 -1.655662 0.765837 106 | v 0.475751 -1.690193 -1.344981 107 | v 0.417265 -2.268152 -0.174915 108 | v 0.816613 -1.826042 -0.435965 109 | v 0.198171 -2.327462 -0.147365 110 | v 0.667988 -1.636359 -1.149996 111 | v 0.795842 -1.716311 -0.802179 112 | v 0.636113 -2.103915 -0.226097 113 | v 0.232324 -1.701587 -1.455695 114 | v 1.208377 -0.413856 -0.464327 115 | v 1.241327 0.111121 -0.479527 116 | v 1.113224 -0.375647 0.201736 117 | v 1.155852 -0.009892 0.181728 118 | v 1.198228 0.309252 -0.085963 119 | v 1.155757 0.161210 0.105486 120 | v 1.116919 -0.704591 -0.038965 121 | v 1.073119 -0.617570 0.160612 122 | v 1.216728 0.282039 -0.250336 123 | v 1.157836 -0.635730 -0.232826 124 | v 1.234213 -0.351310 -0.286095 125 | v 1.256683 0.013661 -0.292317 126 | v 1.323840 0.269692 -0.011425 127 | v 1.191158 -0.763654 0.005345 128 | v 1.424983 0.225411 -0.165862 129 | v 1.309679 -0.681250 -0.175907 130 | v 1.440080 -0.390069 -0.239910 131 | v 1.487426 0.005347 -0.222527 132 | v 1.176485 -0.622691 -0.133370 133 | v 1.279293 -0.346559 -0.195914 134 | v 1.251290 0.200534 -0.165374 135 | v 1.302813 -0.014104 -0.199447 136 | v 1.297737 0.010328 -0.021320 137 | v 1.255109 -0.355427 -0.001312 138 | v 0.000000 -0.453655 -1.659522 139 | v 0.000000 -0.541361 1.644740 140 | v 0.000000 -2.166581 -1.797678 141 | v 0.000000 -2.931921 -0.092391 142 | v 0.000000 -2.022603 -1.682083 143 | v 0.000000 -2.309822 -1.987709 144 | v 0.000000 -3.150186 -0.031994 145 | v 0.000000 -1.194687 -1.471510 146 | v 0.000000 0.128182 -1.702579 147 | v 0.000000 0.083227 1.367990 148 | v 0.000000 -1.385609 1.215298 149 | v 0.000000 1.661600 -0.035653 150 | v 0.000000 1.484891 -0.938427 151 | v 0.000000 1.547083 0.431373 152 | v 0.000000 -1.962361 0.519891 153 | v 0.000000 -1.849640 0.069715 154 | v 0.000000 -2.629138 -0.126451 155 | v 0.000000 -0.855055 -1.534732 156 | v 0.000000 0.693109 -1.621896 157 | v 0.000000 0.666400 1.203721 158 | v 0.000000 -0.951767 1.277723 159 | v 0.000000 1.612926 -0.500365 160 | v 0.000000 -1.962373 -0.104327 161 | v 0.000000 1.246569 0.927286 162 | v 0.000000 1.223347 -1.312165 163 | v 0.000000 -1.812575 1.064772 164 | v -0.794880 -0.456218 -1.453191 165 | v -0.631179 -0.544477 1.149647 166 | v -1.138971 -0.447948 -0.797869 167 | v -1.094424 -0.450260 0.416698 168 | v -0.439387 -0.454233 -1.604853 169 | v -0.277122 -0.541346 1.593228 170 | v -0.962543 -0.496748 0.802866 171 | v -1.031688 -0.430602 -1.168014 172 | v -0.707379 -2.084441 -1.645555 173 | v -0.839524 -2.829275 -0.206489 174 | v -1.397615 -2.236477 -0.621362 175 | v -0.434208 -2.923688 -0.123380 176 | v -1.050709 -1.962676 -1.461045 177 | v -1.293451 -2.041516 -1.087568 178 | v -1.235680 -2.613284 -0.304228 179 | v -0.340290 -2.143095 -1.758874 180 | v -1.115928 -2.194080 -1.851622 181 | v -1.845940 -2.157743 -1.262549 182 | v -0.617709 -2.264206 -1.954206 183 | v -2.010724 -2.349208 -0.716563 184 | v -1.200113 -3.008740 -0.212898 185 | v -1.574329 -2.080434 -1.682444 186 | v -0.570588 -3.111907 -0.052411 187 | v -1.740340 -2.762577 -0.342175 188 | v -0.544184 -1.218010 -1.316640 189 | v -0.785863 0.123222 -1.493973 190 | v -0.631784 0.065619 1.101769 191 | v -0.544759 -1.373754 1.087082 192 | v -1.175014 0.129346 -0.859118 193 | v -0.891132 -1.348171 0.039141 194 | v -0.868947 -1.290124 -0.704046 195 | v -1.139899 0.081269 0.384301 196 | v -0.837687 -1.364774 0.456978 197 | v -0.793231 1.461336 -0.051452 198 | v -0.713654 1.338543 -0.865439 199 | v -0.726249 1.374125 0.384978 200 | v -0.409165 -1.889288 0.476818 201 | v -0.413912 -1.767348 0.077705 202 | v -0.259894 -1.211585 -1.410389 203 | v -0.447793 0.127746 -1.647718 204 | v -0.713996 -0.856596 -1.347879 205 | v -0.752636 0.673336 -1.427497 206 | v -0.212867 0.086756 1.329009 207 | v -0.357345 -1.381319 1.188635 208 | v -0.732227 0.650184 1.043815 209 | v -0.654066 -0.939296 1.125531 210 | v -0.889012 -1.449899 -0.343519 211 | v -1.160249 0.646703 -0.086923 212 | v -1.130602 0.601183 -0.831471 213 | v -1.090904 0.593201 0.273403 214 | v -1.075487 -0.935149 -0.039751 215 | v -1.082869 -0.843542 -0.740058 216 | v -1.020563 -0.873545 0.347217 217 | v -0.772650 1.426925 -0.451773 218 | v -0.482622 1.605282 -0.040162 219 | v -0.426543 1.448078 -0.917558 220 | v -0.443451 1.503576 0.417306 221 | v -0.184642 -1.945136 0.517415 222 | v -0.176943 -1.828176 0.063050 223 | v -0.751049 -1.803618 -1.319485 224 | v -0.268966 -2.611370 -0.157706 225 | v -0.392801 -1.919171 -0.138765 226 | v -0.684180 1.119959 0.843505 227 | v -0.639849 1.116517 -1.197492 228 | v -1.045719 1.150136 -0.052040 229 | v -0.960438 1.120901 -0.785369 230 | v -0.975766 0.100961 0.771586 231 | v -0.742840 -1.359286 0.823574 232 | v -0.379030 -1.751386 0.972500 233 | v -1.068913 0.117326 -1.234360 234 | v -0.748300 -1.229600 -1.130803 235 | v -0.958901 -2.034204 -0.563438 236 | v -0.910357 -1.874164 -0.970900 237 | v -0.685983 -1.642155 0.064883 238 | v -0.545493 -2.517920 -0.209344 239 | v -0.988320 1.116698 0.346230 240 | v -0.810883 -2.329444 -0.291012 241 | v -0.540159 -1.931201 -1.516239 242 | v -0.648070 -1.672735 0.417637 243 | v -0.394747 -0.858695 -1.479472 244 | v -0.428887 0.688365 -1.571638 245 | v -0.281567 0.656137 1.176196 246 | v -0.313651 -0.949308 1.240532 247 | v -1.172400 0.599202 -0.417715 248 | v -1.093921 -0.881204 -0.391642 249 | v -0.470959 1.561494 -0.484958 250 | v -0.183718 -1.955385 -0.116337 251 | v -0.364051 1.225281 0.905220 252 | v -0.365010 1.202835 -1.287747 253 | v -1.028806 1.145044 -0.410251 254 | v -0.970931 0.601422 0.706430 255 | v -0.895122 -0.912495 0.819637 256 | v -0.219718 -1.781411 1.040744 257 | v -1.020569 0.612032 -1.172499 258 | v -0.978438 -0.840622 -1.122864 259 | v -0.275865 -1.996229 -1.638517 260 | v -0.621035 -1.760691 -0.169148 261 | v -0.913952 0.981099 0.682378 262 | v -0.898184 0.993185 -1.079507 263 | v -0.576067 -1.655662 0.765837 264 | v 0.000000 -1.706536 -1.509472 265 | v -0.475751 -1.690193 -1.344981 266 | v -0.417265 -2.268152 -0.174915 267 | v -0.816613 -1.826042 -0.435965 268 | v -0.198171 -2.327462 -0.147365 269 | v -0.667988 -1.636359 -1.149996 270 | v 0.000000 -2.333460 -0.125651 271 | v -0.795842 -1.716311 -0.802179 272 | v -0.636113 -2.103915 -0.226097 273 | v -0.232324 -1.701587 -1.455695 274 | v -1.208377 -0.413856 -0.464327 275 | v -1.241327 0.111121 -0.479527 276 | v -1.113224 -0.375647 0.201736 277 | v -1.155852 -0.009892 0.181728 278 | v -1.198228 0.309252 -0.085963 279 | v -1.155757 0.161210 0.105486 280 | v -1.116919 -0.704591 -0.038965 281 | v -1.073119 -0.617570 0.160612 282 | v -1.216728 0.282039 -0.250336 283 | v -1.157836 -0.635730 -0.232826 284 | v -1.234213 -0.351310 -0.286095 285 | v -1.256683 0.013661 -0.292317 286 | v -1.323840 0.269692 -0.011425 287 | v -1.191158 -0.763654 0.005345 288 | v -1.424983 0.225411 -0.165862 289 | v -1.309679 -0.681250 -0.175907 290 | v -1.440080 -0.390069 -0.239910 291 | v -1.487426 0.005347 -0.222527 292 | v -1.176485 -0.622691 -0.133370 293 | v -1.279293 -0.346559 -0.195914 294 | v -1.251290 0.200534 -0.165374 295 | v -1.302813 -0.014104 -0.199447 296 | v -1.297737 0.010328 -0.021320 297 | v -1.255109 -0.355427 -0.001312 298 | usemtl Material 299 | s off 300 | f 40 5 133 141 301 | f 70 8 1 26 302 | f 83 6 134 153 303 | f 32 50 91 67 304 | f 92 7 2 46 305 | f 95 8 3 52 306 | f 53 4 7 92 307 | f 26 1 5 40 308 | f 79 37 38 74 309 | f 96 78 9 16 310 | f 77 75 10 15 311 | f 73 72 11 14 312 | f 75 61 12 10 313 | f 137 96 16 135 314 | f 72 77 15 11 315 | f 78 60 13 9 316 | f 61 149 136 12 317 | f 60 73 14 13 318 | f 108 101 78 96 319 | f 107 102 75 77 320 | f 106 103 72 73 321 | f 102 104 61 75 322 | f 259 108 96 137 323 | f 103 107 77 72 324 | f 101 105 60 78 325 | f 104 265 149 61 326 | f 105 106 73 60 327 | f 80 39 140 150 328 | f 95 71 25 41 329 | f 41 25 39 80 330 | f 5 80 150 133 331 | f 81 40 141 151 332 | f 8 95 41 1 333 | f 94 70 26 42 334 | f 42 26 40 81 335 | f 89 81 151 157 336 | f 64 42 81 89 337 | f 82 88 156 152 338 | f 43 82 152 142 339 | f 6 43 142 134 340 | f 67 91 45 27 341 | f 7 67 27 2 342 | f 2 27 43 6 343 | f 44 83 153 143 344 | f 93 44 143 158 345 | f 68 92 46 28 346 | f 100 68 28 69 347 | f 69 28 44 93 348 | f 45 63 88 82 349 | f 76 50 48 65 350 | f 70 94 49 29 351 | f 8 70 29 3 352 | f 47 85 51 30 353 | f 97 47 30 74 354 | f 71 95 52 31 355 | f 16 9 17 19 356 | f 15 10 21 24 357 | f 84 90 65 48 358 | f 4 32 67 7 359 | f 79 33 68 100 360 | f 86 154 144 55 361 | f 89 157 145 56 362 | f 56 145 154 86 363 | f 54 86 55 34 364 | f 90 54 34 65 365 | f 64 89 56 35 366 | f 99 64 35 66 367 | f 66 35 54 90 368 | f 74 30 33 79 369 | f 57 146 156 88 370 | f 55 144 146 57 371 | f 76 36 63 98 372 | f 58 93 158 147 373 | f 79 100 69 37 374 | f 65 34 36 76 375 | f 87 59 148 155 376 | f 58 147 148 59 377 | f 97 74 38 62 378 | f 62 38 59 87 379 | f 14 11 20 18 380 | f 10 12 23 21 381 | f 135 16 19 138 382 | f 11 15 24 20 383 | f 9 13 22 17 384 | f 12 136 139 23 385 | f 13 14 18 22 386 | f 38 37 58 59 387 | f 1 41 80 5 388 | f 99 94 42 64 389 | f 98 63 45 91 390 | f 28 46 83 44 391 | f 50 76 98 91 392 | f 99 66 49 94 393 | f 27 45 82 43 394 | f 31 52 85 47 395 | f 33 53 92 68 396 | f 53 33 30 51 397 | f 35 56 86 54 398 | f 36 57 88 63 399 | f 37 69 93 58 400 | f 57 36 34 55 401 | f 46 2 6 83 402 | f 49 66 90 84 403 | f 39 25 101 108 404 | f 97 62 102 107 405 | f 31 47 103 106 406 | f 62 87 104 102 407 | f 140 39 108 259 408 | f 47 97 107 103 409 | f 25 71 105 101 410 | f 87 155 265 104 411 | f 71 31 106 105 412 | f 52 3 109 85 413 | f 3 29 110 109 414 | f 29 49 84 110 415 | f 4 53 116 111 416 | f 50 32 112 114 417 | f 32 4 111 112 418 | f 85 109 119 118 419 | f 109 110 120 119 420 | f 110 84 117 120 421 | f 48 50 114 113 422 | f 53 51 115 116 423 | f 84 48 113 117 424 | f 51 85 118 115 425 | f 127 128 125 124 426 | f 128 130 126 125 427 | f 130 129 123 126 428 | f 118 119 128 127 429 | f 119 120 130 128 430 | f 120 117 129 130 431 | f 131 126 123 121 432 | f 132 131 112 111 433 | f 126 131 132 125 434 | f 125 132 122 124 435 | f 127 124 122 116 436 | f 122 132 111 116 437 | f 129 117 113 114 438 | f 121 114 112 131 439 | f 123 129 114 121 440 | f 118 127 116 115 441 | f 198 141 133 163 442 | f 228 184 159 166 443 | f 241 153 134 164 444 | f 190 225 249 208 445 | f 250 204 160 165 446 | f 253 210 161 166 447 | f 211 250 165 162 448 | f 184 198 163 159 449 | f 237 232 196 195 450 | f 254 174 167 236 451 | f 235 173 168 233 452 | f 231 172 169 230 453 | f 233 168 170 219 454 | f 137 135 174 254 455 | f 230 169 173 235 456 | f 236 167 171 218 457 | f 219 170 136 149 458 | f 218 171 172 231 459 | f 268 254 236 260 460 | f 267 235 233 261 461 | f 266 231 230 262 462 | f 261 233 219 263 463 | f 259 137 254 268 464 | f 262 230 235 267 465 | f 260 236 218 264 466 | f 263 219 149 265 467 | f 264 218 231 266 468 | f 238 150 140 197 469 | f 253 199 183 229 470 | f 199 238 197 183 471 | f 163 133 150 238 472 | f 239 151 141 198 473 | f 166 159 199 253 474 | f 252 200 184 228 475 | f 200 239 198 184 476 | f 247 157 151 239 477 | f 222 247 239 200 478 | f 240 152 156 246 479 | f 201 142 152 240 480 | f 164 134 142 201 481 | f 225 185 203 249 482 | f 165 160 185 225 483 | f 160 164 201 185 484 | f 202 143 153 241 485 | f 251 158 143 202 486 | f 226 186 204 250 487 | f 258 227 186 226 488 | f 227 251 202 186 489 | f 203 240 246 221 490 | f 234 223 206 208 491 | f 228 187 207 252 492 | f 166 161 187 228 493 | f 205 188 209 243 494 | f 255 232 188 205 495 | f 229 189 210 253 496 | f 174 177 175 167 497 | f 173 182 179 168 498 | f 242 206 223 248 499 | f 162 165 225 190 500 | f 237 258 226 191 501 | f 244 213 144 154 502 | f 247 214 145 157 503 | f 214 244 154 145 504 | f 212 192 213 244 505 | f 248 223 192 212 506 | f 222 193 214 247 507 | f 257 224 193 222 508 | f 224 248 212 193 509 | f 232 237 191 188 510 | f 215 246 156 146 511 | f 213 215 146 144 512 | f 234 256 221 194 513 | f 216 147 158 251 514 | f 237 195 227 258 515 | f 223 234 194 192 516 | f 245 155 148 217 517 | f 216 217 148 147 518 | f 255 220 196 232 519 | f 220 245 217 196 520 | f 172 176 178 169 521 | f 168 179 181 170 522 | f 135 138 177 174 523 | f 169 178 182 173 524 | f 167 175 180 171 525 | f 170 181 139 136 526 | f 171 180 176 172 527 | f 196 217 216 195 528 | f 159 163 238 199 529 | f 257 222 200 252 530 | f 256 249 203 221 531 | f 186 202 241 204 532 | f 208 249 256 234 533 | f 257 252 207 224 534 | f 185 201 240 203 535 | f 189 205 243 210 536 | f 191 226 250 211 537 | f 211 209 188 191 538 | f 193 212 244 214 539 | f 194 221 246 215 540 | f 195 216 251 227 541 | f 215 213 192 194 542 | f 204 241 164 160 543 | f 207 242 248 224 544 | f 197 268 260 183 545 | f 255 267 261 220 546 | f 189 266 262 205 547 | f 220 261 263 245 548 | f 140 259 268 197 549 | f 205 262 267 255 550 | f 183 260 264 229 551 | f 245 263 265 155 552 | f 229 264 266 189 553 | f 210 243 269 161 554 | f 161 269 270 187 555 | f 187 270 242 207 556 | f 162 271 276 211 557 | f 208 274 272 190 558 | f 190 272 271 162 559 | f 243 278 279 269 560 | f 269 279 280 270 561 | f 270 280 277 242 562 | f 206 273 274 208 563 | f 211 276 275 209 564 | f 242 277 273 206 565 | f 209 275 278 243 566 | f 287 284 285 288 567 | f 288 285 286 290 568 | f 290 286 283 289 569 | f 278 287 288 279 570 | f 279 288 290 280 571 | f 280 290 289 277 572 | f 291 281 283 286 573 | f 292 271 272 291 574 | f 286 285 292 291 575 | f 285 284 282 292 576 | f 287 276 282 284 577 | f 282 276 271 292 578 | f 289 274 273 277 579 | f 281 291 272 274 580 | f 283 281 274 289 581 | f 278 275 276 287 582 | -------------------------------------------------------------------------------- /hoaxplus/data/data.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-bindata. 2 | // sources: 3 | // ..\..\data\Basehead.obj 4 | // ..\..\data\data\click.png 5 | // DO NOT EDIT! 6 | 7 | package data 8 | 9 | import ( 10 | "bytes" 11 | "compress/gzip" 12 | "fmt" 13 | "io" 14 | "strings" 15 | "os" 16 | "time" 17 | "io/ioutil" 18 | "path" 19 | "path/filepath" 20 | ) 21 | 22 | func bindataRead(data []byte, name string) ([]byte, error) { 23 | gz, err := gzip.NewReader(bytes.NewBuffer(data)) 24 | if err != nil { 25 | return nil, fmt.Errorf("Read %q: %v", name, err) 26 | } 27 | 28 | var buf bytes.Buffer 29 | _, err = io.Copy(&buf, gz) 30 | clErr := gz.Close() 31 | 32 | if err != nil { 33 | return nil, fmt.Errorf("Read %q: %v", name, err) 34 | } 35 | if clErr != nil { 36 | return nil, err 37 | } 38 | 39 | return buf.Bytes(), nil 40 | } 41 | 42 | type asset struct { 43 | bytes []byte 44 | info os.FileInfo 45 | } 46 | 47 | type bindataFileInfo struct { 48 | name string 49 | size int64 50 | mode os.FileMode 51 | modTime time.Time 52 | } 53 | 54 | func (fi bindataFileInfo) Name() string { 55 | return fi.name 56 | } 57 | func (fi bindataFileInfo) Size() int64 { 58 | return fi.size 59 | } 60 | func (fi bindataFileInfo) Mode() os.FileMode { 61 | return fi.mode 62 | } 63 | func (fi bindataFileInfo) ModTime() time.Time { 64 | return fi.modTime 65 | } 66 | func (fi bindataFileInfo) IsDir() bool { 67 | return false 68 | } 69 | func (fi bindataFileInfo) Sys() interface{} { 70 | return nil 71 | } 72 | 73 | var _baseheadObj = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x84\x59\xdb\xae\x5d\xc9\x6d\x7c\xd7\x57\x2c\xc0\x0f\x4e\x1e\xfa\xa8\xc9\xbe\xfb\xd1\x09\x9c\x38\x80\x91\xfc\x41\x20\x79\x8e\x62\x01\x9a\x28\xd1\x65\x26\xce\xd7\xa7\xaa\xd8\x5b\xb2\x4e\x80\x5e\x63\x18\xda\xab\x2f\x6c\x36\x59\x2c\xb2\x79\x7e\x73\xfd\xfe\xc3\xf3\x7f\xfe\xf4\xfc\xe9\xfa\xc5\x9f\xfa\xba\xfe\xee\xf3\xd7\xb7\x57\xfe\xfb\xeb\x5f\x7f\xff\x2f\xd7\x1f\xde\x7f\x78\xfe\xdd\xf5\xdb\xdf\xbf\xf9\xfc\xfc\x97\xe7\x37\x3f\x3d\xbd\xe5\xca\xdf\xbe\xfa\xcd\xf5\xeb\xaf\xbf\xc6\xc7\xf3\xa7\xa7\x8f\x9f\xfe\x03\x23\x7f\x7a\xf3\xd3\xf3\xf5\xf6\xaf\x0f\x61\xff\xf4\xf5\xaf\xff\x7b\xfd\xe5\xcb\x97\xff\xfa\xfc\xbb\xd7\xaf\xb9\xfa\xcb\xd7\x4f\x6f\x3f\x7e\xfe\xef\xaf\xef\x7f\x7a\xfa\xf3\xc7\x9f\x5f\xff\xe1\xeb\x87\x0f\xff\xf6\xe9\xf9\x97\xf7\xcf\xbf\xbe\xfe\x23\x36\xfc\xcf\xd3\x9f\xdf\xfd\xfc\xfa\x8f\xff\xf8\x7a\xe6\xd6\xbb\xbd\xfa\xf9\xcb\x87\x0f\xef\xdf\x5e\xdf\x4e\xc6\xf7\xab\x8f\xd7\x3f\xe3\xe7\xbf\xff\xc3\xd7\xb7\xcf\xaf\x7e\xb9\xf2\xd3\x58\x75\xce\x7c\xa5\xfc\x54\x5b\x77\x9b\x57\x32\xfc\x2a\xb6\x4c\xd3\xbd\x98\x8d\xc5\xe9\x56\x6b\x1d\xe3\xb2\x27\xab\xab\xd7\x81\x59\xfc\x2c\x73\x0d\xd3\xe6\x4a\x41\xfc\x35\xd6\x98\x7d\x69\x3a\xaf\x5a\xbd\x86\xec\xec\x3d\x43\x5e\xb5\xde\xd7\x94\xe8\x5a\x56\x99\x23\x66\xab\x97\xc2\x93\x7b\xae\xb3\x15\x4d\xfb\x18\xe6\x1e\x27\x5b\xa9\x1d\xf2\xda\x2a\xee\xb1\x79\x75\x6f\xb5\x68\xf3\xea\x03\x27\xe7\xa7\x99\x7d\xf6\x1e\x07\x17\xeb\x53\xda\xd4\x92\x7b\x76\x8a\xc6\x48\xb6\x1a\x77\xce\xa3\xf0\x52\xfe\x94\x27\x6e\x65\x3a\xb9\x36\xfc\xa7\xe9\x59\x56\xa3\xda\xfe\x34\x7d\xf9\x68\x94\xe3\xb9\xd7\x19\xb7\x2a\x6b\x74\x6b\x9c\xf6\xd2\x69\x12\x4c\xc3\x72\xa5\xfb\xbe\x56\xf5\x3c\x39\xbd\x30\x1f\x5a\x18\xae\x37\x73\xa8\xd6\xf2\xc8\x8b\x27\xe2\x0a\x7d\x74\xd9\xbb\x5b\xae\x4d\xd3\xbe\x4a\x6d\x26\xd5\xaa\x35\xd3\x74\x9e\xa3\xf5\x19\xd3\x05\xbf\x32\xa7\xbb\x15\x9f\xb2\x6d\xc9\xf5\x61\x95\x52\xb3\x2f\x4d\x1b\x2e\xbe\x1a\x77\x8f\x36\xe7\xa8\xe1\x2e\x6b\xcb\xa5\x9a\xad\x9a\x29\xc7\x9e\x26\x0e\x71\xd7\xf4\xac\x0d\xc3\x9a\x6e\x63\x54\x39\xc4\x69\xe7\x15\x50\xb0\x21\xcd\x71\xef\x8e\x2b\x4a\xb5\xd5\xf8\x0b\xd3\xd0\xd7\xf2\x08\xab\x95\xba\x64\x01\x18\xda\x7a\xeb\x25\x34\xcf\xd9\x0c\x22\xcb\x53\xc6\x7d\xaa\x10\xe7\xe6\x73\xc5\xc5\x1a\xce\xf3\xed\x92\x0c\x13\xca\x25\xd3\xe1\x1d\x9d\xdd\x46\x6e\x34\x65\xc1\x1d\x6c\x65\xd9\x3c\xc3\x49\x66\xda\x0d\x79\x25\x34\x1f\xd0\x37\x5c\x02\x37\xd8\x08\x87\x02\xba\x36\x25\x12\x00\x87\x9e\xfc\x05\x84\xf4\x9a\x03\x0e\xb3\xcd\x5e\x2e\xb9\xc9\x5d\x60\xa9\x0b\x5e\x2e\x8f\x08\x18\xd8\x8c\xf3\x70\x17\x5b\x34\x63\xb6\xd1\xd7\x43\xf2\x68\xf2\x66\x01\xa4\x5a\xbd\xe4\xad\x3c\xc3\xa0\x38\x1f\x90\x93\xe0\x45\x04\x43\xab\xd9\x96\x59\x38\x6b\xe2\x57\xd1\x69\xa5\x4e\x43\x18\xe1\x88\xb2\xac\x46\xe0\xcd\x3e\x57\x1d\xd2\x79\x65\x8b\x28\x1a\xb9\xe6\xda\x77\xe4\xad\xb9\x16\xb7\x4c\xf3\xce\x1f\x65\xc2\xe5\x7b\x6f\x19\x7d\x6a\xaf\x00\x4a\x0d\x10\xdc\x88\xcb\x1d\xf1\xc5\x8b\x5d\x82\x5d\x29\x3d\x4c\x69\xb5\x05\x7c\x07\x80\xac\x7b\x00\xb1\x3b\xc2\x26\x06\x4a\xdc\x77\x00\x0f\x95\x36\x28\xa3\x9a\xb7\x38\xf6\x21\xb8\xe6\x05\x7f\x0b\x54\x13\x38\x63\x4c\x56\x28\xb2\xaf\x8b\x20\x5e\xa6\xeb\x8e\x3e\x8a\x22\x36\x0f\x20\x2a\x5c\xe4\x0d\xf7\xd9\x2e\xb2\x36\x25\xa5\x5a\x2e\x33\xce\x25\xf5\xac\x70\x11\xee\xd3\x23\x60\x41\x10\xf3\xa1\xf3\x5a\xdb\xbc\xbd\xad\x1e\x36\x1d\x73\x6c\xa5\x9b\x77\xdc\x13\xae\x1c\x45\x17\x86\x68\x1f\x50\x3b\x4e\x06\x08\xfb\x90\x25\x3b\xe2\x8c\x77\x83\xc5\x73\x6c\x2d\x0d\xaa\x4a\x99\x32\xad\x84\xf7\x27\xc0\x12\x4a\x0f\xc2\x85\x5b\x3b\xfc\x3c\xe5\xfc\x8a\x75\x31\x09\xa3\xe5\x2e\xa5\x80\x25\x5f\x14\x0c\x8b\x81\x59\xc3\x45\x93\x5e\x95\x2a\x55\x9e\x14\x62\x4b\xb3\xe0\x18\x03\x69\x55\x7a\xb5\xd7\x3e\xb2\xbc\x00\xf5\x40\x28\xdb\xf9\x22\x35\xcc\x22\xa6\x66\xf8\xa8\x58\x1d\xb6\x59\x17\xff\xa3\xcf\x49\x97\xd9\x44\xa5\x88\x8e\xd8\x9a\x01\xd2\x20\xdd\x85\xc3\xaa\xce\x05\xea\x46\xdb\x7b\x27\x8c\xa1\x41\xa0\x09\xb1\x2d\xd4\xd5\x8c\xe0\x8b\x69\x47\x62\x89\xf3\x06\xa6\xe5\xff\x3a\x10\x67\x61\x0e\xa0\xa3\x65\x02\x0b\x88\xf4\x16\xcc\x0e\xd2\x88\x38\xaa\x13\xd8\xf1\x8b\x1c\xdf\x7c\x4a\x32\xe0\x6c\x0f\xd6\x74\x82\x8c\x7b\xeb\xcc\x43\xe4\xb1\x10\x3e\x6d\x63\xa7\x8a\x15\x41\x13\x19\x1e\xe9\x42\xd3\x28\x22\x1e\x40\x62\xd6\x5e\x65\xca\x85\x45\x72\x34\x4e\xad\xdb\x0d\x0c\xd5\xa0\x32\x5c\xcd\xb4\x37\xf7\x02\x16\xde\xd0\x00\xf3\x2a\x7e\x67\x2e\x3d\xb2\x1f\xdc\x8c\x24\x14\xd8\x40\x10\xd2\x87\xa4\x5b\x2b\x43\xa4\x45\x6e\xdc\x27\xc3\xaf\xa0\x13\x9d\x8c\x7c\x19\x89\x10\x29\x71\xf4\x8d\x80\x09\xda\xc9\xe2\xdd\xb5\x1a\xbd\x49\xa3\x6e\xc0\x77\x04\xb0\x22\xc9\x10\x33\xa6\x60\xb5\x05\x50\x06\x73\x20\x1d\x8c\x40\x1b\x90\xf5\x88\x51\xcf\x9b\xb0\x56\x27\xce\x04\x28\xb8\x5a\xc7\x82\xc3\xca\xe6\x24\x38\x73\x74\xde\xd3\x80\xe2\x6e\x72\x0c\x42\x2a\x54\x1e\x15\x59\x23\x08\x90\xb9\x80\xcb\x26\x92\xca\x08\x9a\x45\x56\xcc\x25\x47\xd6\xc0\x45\xba\xa4\x79\xcb\x3b\x75\xc1\x1a\xa6\x20\x84\xf1\x5d\x71\xe4\xc0\x6c\xdf\xa6\xac\xb3\x64\xed\x75\xa0\x3d\x7e\x01\xa8\x33\x87\xfb\x57\x9b\x52\x15\x04\xcf\x04\x29\x3e\x03\x96\x70\x8f\x98\x36\x7a\x56\x8e\x80\xeb\xba\xa6\x17\xd2\x4d\xce\xdb\x94\xe0\x87\xa8\x10\x40\xeb\xad\xc9\x8b\x28\x5c\xca\x66\x61\xe4\xa8\x42\xd9\xb0\x24\x72\x4f\x24\x6c\x50\x6e\xdc\x6a\x61\x9d\x87\x1f\x58\x7c\x08\xb4\xdd\x4b\x48\x9e\x96\xe7\xd4\x5e\x84\x3d\xd2\x8d\xf6\x42\x1b\xf3\x2d\x3a\x5b\x10\xfc\x2a\xe6\xe1\x6c\x66\xcc\xcd\x87\x9d\x68\xd5\x55\xfb\x40\x94\xb5\x40\x66\x2f\x63\xe3\xa3\x8e\x3a\x82\x97\x10\x56\x41\x6a\xa8\x93\xc6\x03\xf3\x13\xa9\x59\xb7\x9b\x25\x88\x13\x5e\xef\xdb\x22\x40\x6b\xeb\x41\x2e\xe0\x69\x15\x5e\xa8\x3b\xd6\x46\x1e\x59\x5a\x8e\x5f\xb8\x79\x26\x16\xbc\xe6\x56\x1e\x79\x07\x1f\x59\x0c\x00\x6b\x28\xd4\xa0\xd5\xb0\xb6\xf9\x01\xb8\xd5\xde\x89\xdc\x11\x9e\x00\x35\xc3\xb0\xa1\x15\xac\xde\x08\x3d\x1c\x5b\x57\x54\x6f\x40\xea\x8e\x44\xd0\xcd\x88\x30\x59\xe0\xb2\xa9\x20\x87\x59\xcb\xe3\xc6\xc8\xa9\x0a\x54\x77\x04\x39\xb1\xb7\x80\x5b\xcf\x7b\xb2\x31\xf9\xb2\x16\xf0\x59\x74\x5f\x9f\xa0\xf3\xb1\xb9\x05\x75\xa8\x58\x12\xc5\x62\xf8\x01\x09\xc0\x9b\x6d\x50\x43\x6d\x0b\xca\x43\xb9\xa3\x22\xae\xd7\x87\x0f\x57\xdb\xa5\x22\x32\x0c\x54\x95\x5b\xd7\xc3\x0b\x8e\xb8\x0a\x95\xc7\x44\x76\x35\x85\x57\x1e\xb5\x7e\xa7\x34\x31\x2d\x6c\x11\xe9\x98\xe6\x5b\x8f\x58\x9a\x8c\x34\xf1\x61\xee\x51\x1b\xe0\xa8\xd9\xeb\xae\x51\xe1\x57\x5d\x04\xf9\xc7\x5d\x38\x81\xff\xda\x26\x44\x54\x85\x39\xee\x39\x7a\xee\x2b\xf8\x01\xff\xd6\x07\xe6\x59\x68\x0a\xa0\x96\x95\xcc\x51\xf1\x94\xf1\xa8\x0e\xa6\xa9\xec\x58\xb8\x77\xe4\xc3\x3c\x16\x8a\xc7\x5d\x0f\x41\xa0\xc2\xa5\x37\x94\xfa\xb2\x47\x6f\x73\xdf\x18\x35\xc9\x68\x51\xd5\x22\xe6\x56\x89\x7c\x88\x3c\x63\x3b\x13\x93\xa8\xa3\x90\x03\xc8\x64\x38\x10\xe6\xda\x8c\x39\x11\x25\xb6\x19\x13\x44\x13\xf0\x01\x5b\x6c\x56\xb3\xa5\x62\x45\x01\x83\x54\x1c\xbb\xeb\x28\x0f\xd2\xeb\x63\xcd\x19\x86\x40\x9e\x94\x49\x80\xa3\xb5\x81\x8b\x2b\xcc\x20\x6b\x22\xdd\x02\x86\x19\x19\x64\xc7\x53\xe9\xaa\x12\x51\x82\xb2\x16\x8a\xd2\x1b\x5a\x3c\x72\x35\x2a\x17\x57\x95\x30\x10\x96\x51\xe2\xa0\x6e\x47\x68\xed\x1a\x13\x16\x88\x17\x05\xd8\xab\x89\x36\x91\x1b\xa0\x67\x4c\x63\x54\xe9\x1a\x35\xa4\x45\x04\x20\x1a\xdb\x9e\x65\x31\x16\x65\x16\x0a\x39\x14\x17\x3c\x0e\x85\x5e\xd9\xf5\x16\x52\x51\x58\x0a\xa4\x8a\xea\x46\xa1\x00\x3b\x46\x5e\x84\x4d\x00\x0a\x42\x3c\x23\xf1\xc5\x32\x30\xd6\x2e\x7d\xb1\x17\x0e\xe1\x0e\x60\xcc\xb2\x98\x19\x59\x78\x0b\x26\x20\xd6\xae\xee\x5a\x60\x84\x65\x4f\xdf\xc1\x3a\x60\x24\x4d\xa3\xfc\x46\x15\x2c\x29\xb9\x5b\x04\xba\x1b\x68\x67\x8a\x2c\x00\x5e\x2d\x03\x5b\x97\x6f\x3a\x8f\x19\xb9\xa3\xb3\x8e\x09\x5a\xc4\xab\xc1\xfb\x7e\x4b\x80\x4a\x95\xcc\x51\x09\x14\x8b\xe9\x09\x5b\x6f\x63\x02\x57\x93\x6c\xcf\x04\xd4\x2d\x78\x11\x85\x63\x58\x0b\x8e\x60\x1e\x61\x8a\x5c\x48\xf6\xd2\xda\x10\x99\x6d\xdb\x83\x45\x9c\x2e\xd5\x55\x4e\xd2\x6c\xad\xec\x37\x0e\xde\x84\x4b\x92\x41\x12\xd5\x76\x54\x20\x90\xe2\x4e\x30\x61\x8f\x17\x27\xc0\x89\xdb\x04\x3a\xdb\xca\x71\x70\x05\xb3\xc5\x7b\xb5\x20\x39\x44\xad\x02\x36\x06\x63\xc7\x34\xd2\x87\xf7\xc7\x79\x23\xf0\xe3\xdf\x7c\x3c\x40\xd8\x2d\x1e\x6c\xfe\x08\x48\xd4\x84\x23\x36\x3b\x92\xc7\x0a\x8b\x54\xc4\x95\x64\x1b\xc8\xd0\xea\xb6\x88\xf1\x75\x45\x5c\x50\xf8\xd6\xbb\xec\xc7\x55\x01\x81\x85\x39\xc1\x50\x16\xb4\x8a\xbc\x5f\x37\xbd\xf9\x42\x21\xa4\x32\x13\xc8\x76\xd9\x06\xb0\x2f\xbe\x4f\x6e\x28\x44\xa2\x08\x6c\x28\xbb\xe2\x7d\x03\xc3\xef\x2c\x94\xf5\x5f\x14\x54\xb0\x67\x8b\xc0\x07\x74\x5f\x4e\xf3\xa9\xdc\xc9\x70\x1d\xe7\xee\x6a\xe1\x31\x8b\x98\xea\x30\xb3\x78\x01\x8f\xf4\xbe\x89\xe6\xfb\x34\x88\x66\x27\x06\xa0\xb8\xec\x06\xc0\xf7\xe9\xcc\x40\x2c\xfb\x61\x96\x77\xea\xfd\x3e\x0d\xbf\xcd\x20\xc8\x35\xf9\x50\xfc\x61\xba\xa8\x90\x99\x51\xc8\x14\x9a\xe5\x87\x69\x22\xa6\xee\x27\x0b\xca\xd8\x66\x3f\x68\xce\xaa\x1f\x64\x18\xac\x81\x8c\x30\xd6\x8f\xb3\xd0\x85\x65\x38\x5f\x3b\x63\xad\xfc\x42\x32\x89\x20\x2f\x05\x4b\xf3\xf5\xc3\x9d\x71\x93\x6e\x3d\x2c\x07\xa2\xee\xad\xfc\x38\x8b\xbc\x37\x03\x24\x0b\x68\xf7\xf1\xe3\x6c\x43\xa6\x14\x8c\x2b\xb2\xf1\x28\x2f\x8e\x45\x3e\x28\x2a\xbe\x50\xd2\xcf\x17\xb6\xe4\x53\x7b\x75\x85\x0f\x10\x3c\x36\xef\x7e\x37\x65\x47\x01\x52\x76\xe3\xa0\xd7\x66\x2f\x9c\x3c\x1b\x8a\xc8\x28\x17\x00\xf1\xe2\x3f\x5a\xa3\xc3\x8d\xd1\x60\x60\x3b\x67\xd3\xee\xf7\x59\xbe\x7a\x23\x07\x17\x14\xee\x2f\x24\x23\x8f\x02\x17\x8a\x04\x14\xf3\x2f\xac\x01\xd2\x59\x2e\x0f\xa2\x20\x7c\xd0\xfd\x8f\x17\x1e\xc2\x3f\xc0\x5f\x5e\x1a\xcb\x11\x50\x4a\xb3\xcb\xc1\x58\xfd\xc5\xa4\x97\x12\x6f\x5d\x00\xde\xfe\x9f\x64\xd2\xc0\x68\x2a\x40\xf1\xe6\xe3\x75\xd3\x4d\xdf\x2a\x9d\x1b\x57\xe9\xa6\x73\x95\xce\xad\xab\x74\xd3\xbb\x4a\xe7\xe6\x55\x3a\x77\xaf\xd2\x4d\xfb\x2a\xdd\xf4\xaf\xd2\x4d\x03\x2b\xdd\x74\xb0\xd2\x4d\x0b\x2b\xdd\xf4\xb0\xd2\x4d\x13\x2b\xdd\x74\xb1\xd2\x4d\x1b\x2b\xdd\xf4\xb1\xd2\x4d\x23\x2b\xdd\x74\xb2\xd2\x4d\x2b\x2b\xdd\xf4\xb2\xd2\x4d\x33\x2b\xdd\x74\xb3\xd2\x4d\x3b\x2b\xdd\xf4\xb3\xd2\xb9\xa1\x95\x8e\x1d\xad\x74\x6e\x69\xa5\x73\x4f\x2b\x9d\x9b\x5a\xe9\xa6\xab\x95\x8e\x6d\xad\x74\xee\x6b\xa5\x73\x63\x2b\x9d\x3b\x5b\xe9\xd8\xda\x4a\xe7\xde\x56\x3a\x37\xb7\xd2\x4d\x77\x2b\x9d\xdb\x5b\xe9\xa6\xbf\x95\xce\x0d\xae\x74\xec\x70\xa5\x73\x8b\x2b\x1d\x7b\x5c\xe9\xdc\xe4\x4a\x37\x5d\xae\x74\x6e\x73\xa5\x73\x9f\x2b\x1d\x1b\x5d\xe9\xa6\xd3\x95\x6e\x5a\x5d\xe9\xdc\xeb\x4a\xe7\x66\x57\x3a\x77\xbb\xd2\xb9\xdd\x95\x8e\xfd\xae\x74\x6e\x78\xa5\x73\xc7\x2b\xdd\xb4\xbc\xd2\x4d\xcf\x2b\xdd\x34\xbd\xd2\xb1\xeb\x95\xce\x6d\xaf\x74\xee\x7b\xa5\x73\xe3\x2b\x1d\x3b\x5f\xe9\xdc\xfa\x4a\xe7\xde\x57\x3a\x37\xbf\xd2\x4d\xf7\x2b\xdd\xb4\xbf\xd2\x4d\xff\x2b\x9d\x1b\x60\xe9\xa6\x03\x96\x8e\x2d\xb0\x74\xd3\x03\x4b\x37\x4d\xb0\x74\xee\x82\xa5\x9b\x36\x58\x3a\xf7\xc1\xd2\xb1\x11\x96\xce\x9d\xb0\x74\x6e\x85\xa5\x9b\x5e\x58\x3a\x37\xc3\xd2\x4d\x37\x2c\x1d\xdb\x61\xe9\xdc\x0f\x4b\xe7\x86\x58\x3a\x76\xc4\xd2\xb9\x25\x96\xce\x3d\xb1\x74\x6e\x8a\xa5\x9b\xae\x58\xba\x69\x8b\xa5\x9b\xbe\x58\x3a\x36\xc6\xd2\xb9\x33\x96\xee\x5a\x63\xdf\x5f\x15\xb0\x56\x8b\x5c\xd9\xf2\xdf\x60\xf1\xd4\x3a\x4b\x37\xbd\xb3\x74\xd3\x3c\x4b\x37\xdd\xb3\x74\xdb\x3e\xfb\x9b\xe7\x36\x9e\x4c\x3d\xc8\xd9\x51\x14\xd8\xa3\x04\x3a\xb4\xd7\xd2\x4d\x7f\x2d\xdd\x34\xd8\xd2\x4d\x87\x2d\x9d\x5b\x6c\xe9\xdc\x63\x4b\xe7\x26\x5b\x3a\x77\xd9\xd2\xb1\xcd\x96\x6e\xfa\x6c\xe9\xdc\x68\x4b\xe7\x4e\x5b\xba\x69\xb5\xa5\x9b\x5e\x5b\x3a\x37\xdb\xd2\xb9\xdb\x96\xce\xed\xb6\x74\xee\xb7\xa5\x9b\x86\x5b\xba\xe9\xb8\xa5\x73\xcb\x2d\xdd\xf4\xdc\xd2\x4d\xd3\x2d\x9d\xbb\x6e\xe9\xa6\xed\x96\xce\x7d\xb7\x74\x6c\xbc\x7d\xfd\xfc\xfc\xf3\x97\x0f\xd7\x9f\xde\x7c\x79\xfe\xf4\xfe\xcd\x87\x57\x9f\xaf\x8f\xef\xde\xbd\x7a\x77\xc1\x11\xed\xc2\x0d\x2e\xbe\x6a\xde\x5d\x80\x0a\x92\xce\x05\x4f\xbf\xbb\x60\x65\x10\x36\x94\xb4\x56\xf0\x09\xe6\x84\x39\x71\xe5\x3e\xf0\x05\xbf\x8d\xcb\xaf\xca\x85\x20\xe4\x79\x95\x0b\x4f\x93\x77\x57\x2b\x57\xc5\xcc\xe2\x6f\xd8\xd1\x20\x1d\xf5\x0e\x04\xaf\x0b\x8a\x83\x67\x71\x55\xec\xe8\x17\xea\x44\xa4\x22\x6e\x47\x08\xb2\x21\x82\xdc\xd1\xf8\x55\xae\x81\x52\x13\x24\xce\x85\x98\x60\x07\x10\x03\x14\xc2\x84\xc9\xb2\x9c\x6a\x69\xad\x73\x33\x02\xdf\xa4\xfb\xbc\xc0\x21\x30\xdf\xc2\x07\x77\xb1\x34\x03\x8e\x8d\xba\x60\x66\xf0\x92\x18\xa1\x20\xa6\x56\xa4\x7e\x2a\xd1\xf5\x0d\x31\x48\xa8\x38\x6e\x0c\x7d\x63\x5b\x96\x26\x23\xd6\x53\x83\x4a\xa9\x83\x07\x3b\xd3\x68\x9e\x52\xa6\xc4\x86\x22\x21\xbc\x8b\xeb\x1b\xe7\xe7\xa6\x63\xa7\xbe\xeb\x45\xaa\xa5\x4e\xdd\x34\xd0\x74\x0a\x94\xea\xbc\x1a\x30\x89\x40\xb4\x4a\x2b\xe4\xb0\x29\x98\x15\xa5\xb8\xdc\x52\xf5\x13\x0b\x26\xe7\x1a\x57\x1b\xeb\xf5\x42\xe5\x90\x8a\xb9\x0d\x6b\xac\x71\xf1\xe4\x66\x7e\x51\x4c\xa5\x4b\xe1\x88\x4a\xad\x40\xa6\xfc\x09\x1f\x6b\xdd\xe2\x56\x63\x4a\x6f\xbc\x03\xea\x34\x2c\xc0\xd0\xa4\x01\x51\xe6\x83\xba\x8d\x2f\x2c\x39\x16\x45\x38\x86\x98\x24\x4c\xb2\x3a\x47\xf0\x93\x08\xe1\xe7\x20\x34\xf0\xb0\x70\x8a\x1a\xfc\x06\xfe\x84\x02\xfe\xc0\x5a\xda\x19\x89\x1f\xa8\x02\x9e\xb0\x93\xaa\x23\x54\x30\x64\x94\xd4\x68\xa6\x3e\x09\x2c\xbc\x19\x3d\x8c\x96\x39\x02\x9c\x77\xb9\x74\xf1\x27\xd6\x2f\x6e\xc5\x51\x78\xd0\x40\xc5\xc9\x43\xf0\x30\x80\x3d\xf0\x54\xed\x2d\x50\x8c\x8b\xc3\xd4\xae\x9b\xc8\x04\xc0\x1f\x77\x8d\x0b\xe1\x8b\x2b\x17\x19\x79\x70\x00\x04\x27\x50\xc2\xde\x30\x1c\x2e\x58\xe4\xa0\x4e\x7c\x02\x16\x14\x61\xc2\x27\x52\x80\x73\x21\xf2\x35\x62\x18\xde\xac\xd4\xb2\x32\x32\x70\xdd\xb1\x51\x5e\xa8\x33\x54\xe7\x42\x1a\x8f\xf7\xab\x57\x6b\x61\x71\x98\x1a\xdf\x38\x86\xe6\xa0\x71\xf1\x9b\x6b\xa6\xbe\xf9\x2f\x96\x5e\x32\x29\xce\xc0\x00\xe2\x4f\x77\x82\x77\xb0\x1d\x3b\x84\x7c\xd4\x11\x18\x40\xd9\xd1\xb9\xaf\x73\x94\x8b\x97\x42\xad\xf2\x4a\xd0\x63\x50\x75\x9d\xd7\xe5\xc8\x49\x75\x5b\x93\x3e\x1c\x93\xd7\x61\x39\xc4\x08\x4c\xb9\x34\x3b\xe9\x13\xf2\x2f\xf2\x78\x5c\x48\x5e\x60\xf4\xf2\x20\xaa\xc6\xf5\x43\x04\x31\x2e\xc6\x41\x25\x4c\x5a\x6c\xc6\x2e\x0d\xb4\x15\xe6\xa5\x2e\x70\x8a\x00\xe3\xfc\x89\x0d\x53\x01\x53\x19\xe3\xac\xe2\xc3\xd3\x8c\x71\x2f\x30\xb1\xc2\xbc\x29\xc6\x19\xbe\x9a\x25\x42\x61\x7a\x2c\xa7\x54\x86\x38\x5f\xa3\x12\xe3\x11\xe2\x08\x0d\x57\xa0\x46\x8c\x03\x34\x3c\x12\xe7\x81\x31\xda\x56\xc7\x18\x14\x88\x9c\x6d\xbf\x25\xb8\x77\x59\x7a\xd2\x00\xf0\xc4\xe2\xf1\x04\x59\x27\x4e\x2b\xe7\x00\x2b\x98\x08\x2b\x34\x47\xbb\x77\x22\x6b\x71\x8e\xc0\x6e\x0c\x0b\x81\x19\xf5\x2b\xc0\x03\x78\xc9\x72\xb0\x3f\x60\x0e\x34\xf7\x19\xb4\x88\x01\xb8\x45\x11\x4a\x67\x75\xb9\x9a\x52\x0a\x3d\x41\x24\x77\x49\x19\xb4\x36\x9c\xa0\x80\xc0\x04\xa6\xcb\xc6\x0f\x03\xe3\xa2\x6a\xfc\x2d\x4d\x80\x91\x29\x19\x8b\x04\x11\xac\x33\xc3\xf4\xdd\x37\x6d\x8d\xd0\x8d\x9e\x11\x4b\xf5\x70\xc6\x1c\x62\x25\xac\x91\x3b\x82\x7f\xc0\x69\x2e\x63\x61\xf5\x1a\x9b\x17\x8b\x18\x8f\xf1\x11\xac\x65\xe1\x7b\x3e\xdc\xc4\x69\x79\x47\x0f\xdb\x55\x22\x4e\x81\x01\xee\xbe\x98\x8d\x26\xbf\x0a\x03\xd0\xe8\xe6\x4c\xe9\xf8\x80\xfe\x6c\xc0\x88\xd7\x2b\xed\x63\x74\xb9\x88\x1c\x26\x2f\x4c\x01\xfc\x7f\x8d\xcc\xc3\x95\xc6\x21\x1e\xdd\x24\x97\x05\x8e\x6d\xf4\x6c\xd9\x84\x53\xc4\xab\xe5\x90\x3e\x38\xc8\x13\x26\xa5\x9a\x50\x57\xc2\x1f\xe4\x3d\xa5\x8e\x1e\x21\x4d\x18\x13\x3e\xc2\x15\x26\x79\x8c\x71\xac\x09\x67\x94\x84\x2f\x5a\x59\x24\xa0\xaf\xc2\x43\x99\x61\xb4\x46\x5f\x50\x05\x38\x36\x65\xd0\xd8\xbf\xf6\x4e\xe1\x55\x5f\x79\xaf\x95\xf2\x52\x7a\xc4\xce\x12\x59\xce\xb6\x54\xca\x89\x80\xf0\x18\x95\x49\x34\xc2\x79\x8e\x70\x26\x4e\xa7\x66\xfa\xf2\x6f\x1a\x52\x6a\x8d\x11\xdd\x52\xbf\x64\x5a\xfb\x36\xb2\xe2\x74\xdd\x3c\x76\xd9\xb6\x93\xce\xd4\x48\xd9\xeb\xea\x43\x1f\x9b\x21\x5d\x2e\xd3\xe9\x6b\x46\x06\x62\x25\x21\x1c\xb3\xb8\x55\x8b\x8d\x14\x21\x96\x72\x65\xa8\x12\x25\x85\xe2\xce\x00\x5f\x14\x57\x17\xdb\x66\x2e\xdc\xb2\x62\xe3\xcb\xd5\x98\xc9\x7b\xa4\x5a\xb2\x02\xbf\xec\x21\x87\xc4\xd1\x34\x7f\x99\xa8\x45\xe7\x50\x83\x4e\xee\x12\xc0\x10\x46\xce\x9b\x32\x43\xaf\x90\x83\x35\x83\x92\x39\x23\x39\xa4\x19\x16\x05\xcc\x31\xca\xa4\x6a\xb8\xb2\xfa\x60\xaa\x91\x2f\xbc\xc4\xbc\x31\x85\x04\xb4\xf8\x68\xd7\xce\x4a\x99\x5a\x93\xb5\x83\xb2\x5c\xec\xec\xa4\x24\xfe\xe9\x89\x29\x5c\x10\x75\xfa\x7d\xe4\x20\xab\x2a\x0d\x69\xc3\x11\xe7\xb9\xec\xec\xd4\x03\x5a\x72\xb7\xab\x28\x70\xe9\xda\xa4\x85\xf7\x58\xd3\xa5\x25\xcf\xf4\x1e\x45\x96\xc5\x3c\x4e\xf0\xb0\x3c\x2d\xce\xfb\x53\x96\xd8\x07\x2b\x63\x07\x65\xa9\x7c\xe3\x1f\x81\x74\x0e\xa9\x52\xbe\xc0\xde\xd0\x92\xbe\x08\xcb\x23\xc3\x68\x9e\xe7\x85\xe5\xd9\xae\xa2\xe5\x59\x6f\xac\xb1\xbd\x83\x02\xf5\x62\x43\xd3\x95\x6d\xf9\xa5\x75\xa4\x0f\x31\x95\xbc\x42\x3b\xd2\xb7\x22\x74\x27\xd5\x30\xfe\x88\x08\xe5\x1d\x78\x36\x90\xc2\xdd\x2d\x6e\x01\x9d\xf9\x07\x35\xf8\xd6\x15\x29\xfc\xd2\xce\x25\x64\x09\x51\x23\x12\x6a\xa3\x0d\x64\x55\x20\x9c\xa3\x5c\xe7\xca\xc1\xae\x92\xca\x95\x00\x5d\xe5\x2a\x7b\x3e\x2a\x5c\x78\x82\x8a\x53\xb6\xa8\x84\x4a\x16\x48\xa2\x37\xa2\x92\xcf\x71\x3c\x8f\x88\x4e\xad\x69\x81\x4a\x8e\x46\xc4\x09\xa3\x35\xe4\x89\xe5\xd8\x93\x89\x62\x86\xbb\x24\x47\xd5\xd5\xd4\xa8\x8b\x69\x9d\x91\x0b\xea\x27\xca\x5d\xa5\x9e\x37\xa6\xab\x11\xa3\xe2\x0d\x7e\x71\xa7\xe4\xcd\xd0\xb9\xe8\x26\xce\x14\xa0\x18\xc4\x23\x0c\xbf\x28\xb5\x3f\x62\x47\x11\x87\x9d\x99\xbb\xfd\x61\x55\xc6\x0d\x47\xb7\x0d\x79\x2f\x9c\x07\xca\xf4\x1a\x76\x6e\x11\x29\x1a\xd5\x2d\x18\xeb\xa8\x35\x18\x75\xe1\x0b\xc5\x0d\x2b\x6d\xd6\xe9\xc2\x8f\xe2\x86\xf5\x20\x0b\x84\xc0\x98\x6c\xd7\x43\xa7\x1a\x3e\x75\x59\x4d\xb6\x5c\xf9\x11\x95\xba\x2d\xa3\x52\xb7\xa8\xc4\x58\x89\x92\xa4\x3d\x7c\xea\x64\x1a\x15\x46\xc2\x18\xbe\x3c\xe6\x39\xaa\x11\x46\xb6\x6b\xa7\xd7\xd8\x35\x75\x72\x8c\xfa\x46\x02\x5b\x1e\xb1\x3b\xb0\x4a\x2b\xd4\x18\xf5\x58\x53\x63\xa7\xe4\x05\x03\xb8\xb4\x84\x76\xb4\x88\xce\x6a\xb2\xba\x2a\xe1\xc0\x0f\x4f\x65\xe6\xe0\x68\x9c\x4e\x5f\x34\x79\x06\x3b\x35\x42\x66\x14\x3a\x19\xd3\xb6\xef\x0e\x26\xda\xbe\x0d\x7f\x15\xed\xc4\x0e\xea\xad\x5b\x34\x65\x55\x93\x4e\x63\xcb\x71\x8b\x9a\xca\xf6\x2d\x28\x23\x8b\xd9\xa0\xad\xe4\x64\xed\xd4\xba\x78\xce\x90\xc3\x50\xb5\xd8\x20\x27\x06\x7a\x67\xf8\x8a\x15\xff\x88\x2c\xc3\x7c\x31\xb7\x5f\xc5\xc6\xe2\xb0\xb9\xfd\x1a\x91\xbb\x7d\xce\xf7\xc6\x10\xf3\x93\xc3\x28\xa3\xe8\x6d\xa5\x11\xdb\xf3\x3c\x4f\xa8\x5b\xa1\xb3\x6c\x20\xee\x8d\x1c\x50\x36\x2f\xac\x6f\xbe\x88\x08\x0f\xac\xca\x7e\xca\x03\x65\x23\x3c\xa2\xc4\x95\x37\x5c\x35\x06\xff\x7a\xa9\x35\x5c\xab\x1a\x59\x72\xc4\x14\xf2\xad\x76\x35\x45\xa4\xa2\x45\xa5\x8b\xb0\x9c\xe9\x45\x65\x12\x69\x68\xc2\xa0\xb2\x8d\xf2\xa9\x72\x0a\xcb\x08\xbe\x79\x84\xcc\xc0\x8e\x6f\x84\x46\xb6\xaa\xf2\xb0\x62\x70\x67\xbd\x16\x1e\x62\xac\xfa\x78\xa0\xc5\x02\x87\x1b\x09\x8c\x73\xf2\x5c\x57\x56\xd3\xc8\x88\x68\x11\x66\x43\xf2\x08\xf6\x17\xbf\x7c\x8b\x4a\xb2\x3f\xd9\x5d\x15\x8c\x6e\x41\xf6\x27\x97\xef\x38\xcd\x31\x4f\xcb\x2a\x2e\xc8\xcb\x64\x7f\xca\xda\xfc\xcc\x7b\x73\x07\x65\xa9\x86\x14\x53\x93\xfd\xc9\x5c\x62\x6c\x61\x87\x32\x7a\xdb\x85\x3c\x19\x40\xf3\xe4\x8f\x19\xd9\x2a\x87\xfd\x88\x91\x78\xc0\xea\x64\xac\x13\x22\xc4\x09\x64\x19\xe6\x49\x31\xc1\xd8\x0c\xe0\xcc\x81\xa3\x3f\xec\x4c\x0f\x32\x73\x12\xa1\x2b\x3f\x6a\x80\x11\xeb\x22\x9b\xeb\x9c\xc1\x75\xd4\x42\xa7\xeb\x1c\x3e\xff\xb8\x56\x36\xe4\x39\xfa\x92\x2d\x25\x19\x67\x30\x03\x53\x7a\xf0\xa1\xed\x93\x07\x11\xb1\x1e\x1c\xc5\x1d\x43\xbc\xa9\x5d\x2b\xe6\x79\x5e\xf0\x21\x6f\xc1\x9c\x43\x1c\x05\x03\x90\x19\xf5\x05\x59\xc1\x63\xd4\x99\x5f\xb4\x65\xd8\x87\xfb\xb5\x53\x7a\x6b\x64\xc5\x97\xd6\x6a\x17\xf5\xd5\x17\xcf\x94\x77\x88\xc3\x69\x5b\x8e\xf4\x59\x0f\x8b\x39\x67\xb5\xab\xc7\xe9\xeb\xfb\x48\xdb\x1a\x72\xc4\x1f\x3a\xf3\xa6\x1c\x89\xcc\xc8\x5f\xba\xbb\x7d\x5b\xb3\xb6\xe5\xcb\xe3\x74\x9e\xbc\xf6\x59\x23\x76\x95\x18\xe5\xba\xef\xf7\x92\x7d\x28\x7d\xbc\xfa\xbf\x00\x00\x00\xff\xff\x59\x87\xfb\x46\x1b\x36\x00\x00") 74 | 75 | func baseheadObjBytes() ([]byte, error) { 76 | return bindataRead( 77 | _baseheadObj, 78 | "Basehead.obj", 79 | ) 80 | } 81 | 82 | func baseheadObj() (*asset, error) { 83 | bytes, err := baseheadObjBytes() 84 | if err != nil { 85 | return nil, err 86 | } 87 | 88 | info := bindataFileInfo{name: "Basehead.obj", size: 13851, mode: os.FileMode(438), modTime: time.Unix(1552225649, 0)} 89 | a := &asset{bytes: bytes, info: info} 90 | return a, nil 91 | } 92 | 93 | var _dataClickPng = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xea\x0c\xf0\x73\xe7\xe5\x92\xe2\x62\x60\x60\xe0\xf5\xf4\x70\x09\x62\x60\x60\x2a\x00\xb2\x3f\xb0\x00\x09\x86\x84\xe5\xaf\xfb\x81\x02\x7c\x9e\x2e\x8e\x21\x15\x8c\x6f\xef\x3a\x7a\x1d\x0e\x10\x68\x78\xd8\xf0\x8e\xeb\x86\xf2\x52\x03\xf7\x8f\x66\x72\xee\xe5\xfc\x2f\xa6\x72\x95\x68\xba\x04\x4a\x58\x07\xc9\xf1\x07\xa9\x5c\x7a\x1f\xdd\x96\x29\x5b\xc7\x22\x14\x7c\xf9\xa5\x18\x03\x04\x74\x24\xd5\x57\xd6\xcc\xdb\xc5\xe6\xfe\x78\xfb\xe1\xae\xb7\x66\x29\x9a\xcf\x4e\x3f\x5e\xbe\x32\xbb\xa0\xe4\xe7\xdb\x3d\x45\xd6\x0f\x5b\x84\xfd\x2c\x20\x84\x9c\xf2\x22\xd1\xf3\x82\xed\xf3\xa5\x2c\xb7\xa8\xc5\xfc\xfe\x66\xf9\xe5\xde\xbe\x3d\xeb\x6e\xe7\x71\xcf\x9b\x96\x3a\xb3\x2b\xf9\x9f\x53\x4a\xaf\x8c\x14\xf7\x75\x49\xab\xfb\x79\xbb\xbd\x2c\xe0\xfa\x76\x4c\xb7\xb1\x3f\x2b\xb5\xfb\xda\x2d\xb7\x43\x4f\xcb\xbe\x2e\xa9\xf9\x3b\x7f\xe5\xf5\xd6\xd7\x57\x6e\x38\xec\x78\x78\xf4\xb7\x37\x97\xfa\x9c\x82\x43\x9c\x20\xe2\xae\xff\x8e\xaa\x4d\x92\x2f\x0b\x52\x97\xb7\x77\xdc\xe8\x6f\xaf\xee\x09\x89\x9f\x9a\x7e\x2f\xf0\x8d\xbe\x78\x7f\xc7\xce\xcf\x7e\x95\x39\xff\x7e\xfe\x14\xfc\xd8\xfe\xf0\xd5\x85\x49\xe7\xc2\x0f\xbd\xfd\x74\xec\x40\xc0\x8e\xe5\xaa\x67\x9b\xbf\xcb\x9c\xe2\xf6\xdf\x5b\x7e\x6a\xe1\x27\x3f\x88\x41\x5f\x39\xa7\x83\xed\x7e\x55\xe1\x76\xea\xa2\x8f\xb1\xc0\xba\x67\x72\xfb\xe6\x4e\x70\xde\x79\xf2\xc6\x85\x5f\xde\x7a\x48\xfe\x69\xf1\xfe\xba\xe4\xcf\xf9\xc3\x4f\x1b\x55\xc0\x6e\xb8\xc3\x00\x11\x2e\xda\xca\x1b\x63\xf9\x65\xfb\x6b\x73\x4b\x84\xe3\x80\xb2\x3f\x26\xdb\x6e\xfb\x77\x73\x62\x54\xd2\x9f\x53\x8a\xfc\x1c\x08\x99\xf2\xe7\x0f\x19\x50\x40\xc2\xe7\x4f\xde\xea\x91\xff\x9f\x2e\x03\x2b\xca\x9d\x0f\x54\xe3\x9e\xfd\x36\xf7\xcf\xee\x6b\x1f\x6b\xe4\xc0\x56\x80\x42\xe5\x7f\xfe\xf5\xa8\xee\x8e\xc3\x47\x36\xfc\x9a\x73\xa6\x5a\x11\x64\x50\x3b\xc8\x44\x99\x9f\x12\x95\x60\x57\x08\x80\x1c\x58\xfc\xb4\x01\x62\x0d\xc8\x28\xbb\x92\x66\x04\xe7\xdf\xdb\x20\x0b\x88\x27\x9c\x2d\x10\xf6\xff\x48\xb7\x63\x98\x2a\xe7\x94\xb5\x26\x7b\x52\x32\x88\xef\xe9\xea\xe7\xb2\xce\x29\xa1\x09\x10\x00\x00\xff\xff\x3a\xf3\x77\xa7\x47\x02\x00\x00") 94 | 95 | func dataClickPngBytes() ([]byte, error) { 96 | return bindataRead( 97 | _dataClickPng, 98 | "data/click.png", 99 | ) 100 | } 101 | 102 | func dataClickPng() (*asset, error) { 103 | bytes, err := dataClickPngBytes() 104 | if err != nil { 105 | return nil, err 106 | } 107 | 108 | info := bindataFileInfo{name: "data/click.png", size: 583, mode: os.FileMode(438), modTime: time.Unix(1552243820, 0)} 109 | a := &asset{bytes: bytes, info: info} 110 | return a, nil 111 | } 112 | 113 | // Asset loads and returns the asset for the given name. 114 | // It returns an error if the asset could not be found or 115 | // could not be loaded. 116 | func Asset(name string) ([]byte, error) { 117 | cannonicalName := strings.Replace(name, "\\", "/", -1) 118 | if f, ok := _bindata[cannonicalName]; ok { 119 | a, err := f() 120 | if err != nil { 121 | return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) 122 | } 123 | return a.bytes, nil 124 | } 125 | return nil, fmt.Errorf("Asset %s not found", name) 126 | } 127 | 128 | // MustAsset is like Asset but panics when Asset would return an error. 129 | // It simplifies safe initialization of global variables. 130 | func MustAsset(name string) []byte { 131 | a, err := Asset(name) 132 | if (err != nil) { 133 | panic("asset: Asset(" + name + "): " + err.Error()) 134 | } 135 | 136 | return a 137 | } 138 | 139 | // AssetInfo loads and returns the asset info for the given name. 140 | // It returns an error if the asset could not be found or 141 | // could not be loaded. 142 | func AssetInfo(name string) (os.FileInfo, error) { 143 | cannonicalName := strings.Replace(name, "\\", "/", -1) 144 | if f, ok := _bindata[cannonicalName]; ok { 145 | a, err := f() 146 | if err != nil { 147 | return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) 148 | } 149 | return a.info, nil 150 | } 151 | return nil, fmt.Errorf("AssetInfo %s not found", name) 152 | } 153 | 154 | // AssetNames returns the names of the assets. 155 | func AssetNames() []string { 156 | names := make([]string, 0, len(_bindata)) 157 | for name := range _bindata { 158 | names = append(names, name) 159 | } 160 | return names 161 | } 162 | 163 | // _bindata is a table, holding each asset generator, mapped to its name. 164 | var _bindata = map[string]func() (*asset, error){ 165 | "Basehead.obj": baseheadObj, 166 | "data/click.png": dataClickPng, 167 | } 168 | 169 | // AssetDir returns the file names below a certain 170 | // directory embedded in the file by go-bindata. 171 | // For example if you run go-bindata on data/... and data contains the 172 | // following hierarchy: 173 | // data/ 174 | // foo.txt 175 | // img/ 176 | // a.png 177 | // b.png 178 | // then AssetDir("data") would return []string{"foo.txt", "img"} 179 | // AssetDir("data/img") would return []string{"a.png", "b.png"} 180 | // AssetDir("foo.txt") and AssetDir("notexist") would return an error 181 | // AssetDir("") will return []string{"data"}. 182 | func AssetDir(name string) ([]string, error) { 183 | node := _bintree 184 | if len(name) != 0 { 185 | cannonicalName := strings.Replace(name, "\\", "/", -1) 186 | pathList := strings.Split(cannonicalName, "/") 187 | for _, p := range pathList { 188 | node = node.Children[p] 189 | if node == nil { 190 | return nil, fmt.Errorf("Asset %s not found", name) 191 | } 192 | } 193 | } 194 | if node.Func != nil { 195 | return nil, fmt.Errorf("Asset %s not found", name) 196 | } 197 | rv := make([]string, 0, len(node.Children)) 198 | for childName := range node.Children { 199 | rv = append(rv, childName) 200 | } 201 | return rv, nil 202 | } 203 | 204 | type bintree struct { 205 | Func func() (*asset, error) 206 | Children map[string]*bintree 207 | } 208 | var _bintree = &bintree{nil, map[string]*bintree{ 209 | "Basehead.obj": &bintree{baseheadObj, map[string]*bintree{ 210 | }}, 211 | "data": &bintree{nil, map[string]*bintree{ 212 | "click.png": &bintree{dataClickPng, map[string]*bintree{ 213 | }}, 214 | }}, 215 | }} 216 | 217 | // RestoreAsset restores an asset under the given directory 218 | func RestoreAsset(dir, name string) error { 219 | data, err := Asset(name) 220 | if err != nil { 221 | return err 222 | } 223 | info, err := AssetInfo(name) 224 | if err != nil { 225 | return err 226 | } 227 | err = os.MkdirAll(_filePath(dir, path.Dir(name)), os.FileMode(0755)) 228 | if err != nil { 229 | return err 230 | } 231 | err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) 232 | if err != nil { 233 | return err 234 | } 235 | err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) 236 | if err != nil { 237 | return err 238 | } 239 | return nil 240 | } 241 | 242 | // RestoreAssets restores an asset under the given directory recursively 243 | func RestoreAssets(dir, name string) error { 244 | children, err := AssetDir(name) 245 | // File 246 | if err != nil { 247 | return RestoreAsset(dir, name) 248 | } 249 | // Dir 250 | for _, child := range children { 251 | err = RestoreAssets(dir, path.Join(name, child)) 252 | if err != nil { 253 | return err 254 | } 255 | } 256 | return nil 257 | } 258 | 259 | func _filePath(dir, name string) string { 260 | cannonicalName := strings.Replace(name, "\\", "/", -1) 261 | return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) 262 | } 263 | 264 | -------------------------------------------------------------------------------- /hoaxplus/data/data/click.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/klauspost/gad/8074eba3799ada5ddcd8a71f794efa03de654fb9/hoaxplus/data/data/click.png -------------------------------------------------------------------------------- /hoaxplus/data/inject.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import "github.com/klauspost/gfx" 4 | 5 | func init() { 6 | gfx.AddData(Asset) 7 | } 8 | -------------------------------------------------------------------------------- /hoaxplus/primitive/line.go: -------------------------------------------------------------------------------- 1 | package primitive 2 | 3 | import ( 4 | "fmt" 5 | "image" 6 | ) 7 | 8 | type Line struct { 9 | P1, P2 Point2D 10 | } 11 | 12 | func (l Line) Draw(dst *image.Gray, col byte) { 13 | w, h := dst.Rect.Dx(), dst.Rect.Dy() 14 | if !l.clip(w, h) { 15 | return 16 | } 17 | 18 | // Find which direction is longer 19 | yLonger := false 20 | shortLen := l.P2.Y - l.P1.Y 21 | longLen := l.P2.X - l.P1.X 22 | 23 | // Swap if we have to 24 | if absf(shortLen) > absf(longLen) { 25 | shortLen, longLen = longLen, shortLen 26 | yLonger = true 27 | } 28 | 29 | // Find how much we should increment on every pixel. 30 | var decInc float32 31 | if longLen != 0 { 32 | decInc = shortLen / longLen 33 | } 34 | 35 | // Small rounding adjustment to avoid accumulated errors. 36 | const sigma = 0.001 37 | 38 | if yLonger { 39 | if longLen > 0 { 40 | // Top to bottom, one y per loop 41 | l.P2.Y += sigma 42 | for j := l.P1.X; l.P1.Y <= l.P2.Y; l.P1.Y++ { 43 | setGray(dst, j, l.P1.Y, col) 44 | j += decInc 45 | } 46 | return 47 | } 48 | // Bottom to top, one y per loop 49 | l.P2.Y -= sigma 50 | for j := l.P1.X; l.P1.Y >= l.P2.Y; l.P1.Y-- { 51 | setGray(dst, j, l.P1.Y, col) 52 | j -= decInc 53 | } 54 | return 55 | } 56 | if longLen > 0 { 57 | // Left to right, one X per loop 58 | l.P2.X += sigma 59 | for j := l.P1.Y; l.P1.X <= l.P2.X; l.P1.X++ { 60 | setGray(dst, l.P1.X, j, col) 61 | j += decInc 62 | } 63 | return 64 | } else { 65 | l.P2.X -= sigma 66 | // Right to left, one X per loop 67 | for j := l.P1.Y; l.P1.X >= l.P2.X; l.P1.X-- { 68 | setGray(dst, l.P1.X, j, col) 69 | j -= decInc 70 | } 71 | } 72 | } 73 | 74 | func (l Line) DrawAA(dst *image.Gray, col byte) { 75 | w, h := dst.Rect.Dx(), dst.Rect.Dy() 76 | if !l.clip(w, h) { 77 | return 78 | } 79 | 80 | yLonger := false 81 | shortLen := l.P2.Y - l.P1.Y 82 | longLen := l.P2.X - l.P1.X 83 | if absf(shortLen) > absf(longLen) { 84 | shortLen, longLen = longLen, shortLen 85 | yLonger = true 86 | } 87 | var decInc float32 88 | if longLen != 0 { 89 | decInc = shortLen / longLen 90 | } 91 | const sigma = 0.001 92 | 93 | if yLonger { 94 | if longLen > 0 { 95 | // Top to bottom, one y per loop 96 | l.P2.Y += sigma 97 | for j := l.P1.X; l.P1.Y <= l.P2.Y; l.P1.Y++ { 98 | setGrayHorizontalAA(dst, j, l.P1.Y, col) 99 | j += decInc 100 | } 101 | return 102 | } 103 | // Bottom to top, one y per loop 104 | l.P2.Y -= sigma 105 | for j := l.P1.X; l.P1.Y >= l.P2.Y; l.P1.Y-- { 106 | setGrayHorizontalAA(dst, j, l.P1.Y, col) 107 | j -= decInc 108 | } 109 | return 110 | } 111 | if longLen > 0 { 112 | // Left to right, one X per loop 113 | l.P2.X += sigma 114 | for j := l.P1.Y; l.P1.X <= l.P2.X; l.P1.X++ { 115 | setGrayVerticalAA(dst, l.P1.X, j, col) 116 | j += decInc 117 | } 118 | return 119 | } 120 | // Right to left, one X per loop 121 | l.P2.X -= sigma 122 | for j := l.P1.Y; l.P1.X >= l.P2.X; l.P1.X-- { 123 | setGrayVerticalAA(dst, l.P1.X, j, col) 124 | j -= decInc 125 | } 126 | } 127 | 128 | // setGray will set a pixel. 129 | // It is assumed that X and y are clipped. 130 | func setGray(img *image.Gray, x, y float32, col byte) { 131 | img.Pix[roundP(x)+roundP(y)*img.Stride] = col 132 | } 133 | 134 | // setGray will set a pixel. 135 | // It is assumed that X and y are clipped. 136 | func setGrayAA(img *image.Gray, x, y float32, col byte) { 137 | // Convert to fixed point 138 | xx, yy := int(256*x), int(256*y) 139 | 140 | // Calculate weights 141 | x1, y1 := xx&255, yy&255 142 | x0, y0 := 256-x1, 256-y1 143 | 144 | // Apply weighted color to pixel 145 | weight := func(pix *byte, w int) { 146 | wOrg := 256 - w 147 | p := int(*pix)*wOrg + int(col)*w 148 | *pix = byte(p >> 8) 149 | } 150 | 151 | // Remove fraction from pixels coordinates. 152 | xx >>= 8 153 | yy >>= 8 154 | 155 | // Check if we can write to the next pixel 156 | xOK, yOK := xx < img.Rect.Dx()-1, yy < img.Rect.Dy()-1 157 | 158 | // Pre-multiply with image stride 159 | yy *= img.Stride 160 | 161 | // draw topleft pixel. 162 | p := &img.Pix[xx+yy] 163 | weight(p, (x0*y0)>>8) 164 | if xOK { 165 | p = &img.Pix[1+xx+yy] 166 | weight(p, (x1*y0)>>8) 167 | } 168 | if yOK { 169 | yy += img.Stride 170 | p = &img.Pix[xx+yy] 171 | weight(p, (x0*y1)>>8) 172 | if xOK { 173 | p = &img.Pix[1+xx+yy] 174 | weight(p, (x1*y1)>>8) 175 | } 176 | } 177 | } 178 | 179 | // setGrayHorizontalAA will set a pixel, but only do AA vertically. 180 | // This can be used when each pixel is drawn horizontally. 181 | // It is assumed that X and y are clipped. 182 | func setGrayHorizontalAA(img *image.Gray, x, y float32, col byte) { 183 | // Convert to fixed point 184 | xx, yy := int(256*x), int(256*y) 185 | 186 | // Calculate weights 187 | x1 := xx & 255 188 | x0 := 256 - x1 189 | 190 | // Apply weighted color to pixel. 191 | // w0 is color weight, w1 is existing weight. 192 | // Sum of w0 and w1 must be <= 256 193 | weight := func(pix *byte, w0, w1 int) { 194 | p := int(*pix)*w1 + int(col)*w0 195 | *pix = byte(p >> 8) 196 | } 197 | 198 | // Remove fraction from pixels coordinates. 199 | xx >>= 8 200 | yy >>= 8 201 | 202 | // Check if we can write to the next pixel 203 | xOK := xx < img.Rect.Dx()-1 204 | 205 | // Pre-multiply with image stride 206 | yy *= img.Stride 207 | 208 | // draw topleft pixel. 209 | p := &img.Pix[xx+yy] 210 | weight(p, x0, x1) 211 | if xOK { 212 | p = &img.Pix[1+xx+yy] 213 | weight(p, x1, x0) 214 | } 215 | } 216 | 217 | // setGrayVerticalAA will set a pixel, but only do AA horizontally. 218 | // This can be used when each pixel is drawn vertically. 219 | // It is assumed that X and y are clipped. 220 | func setGrayVerticalAA(img *image.Gray, x, y float32, col byte) { 221 | // Convert to fixed point 222 | xx, yy := int(256*x), int(256*y) 223 | 224 | // Calculate weights 225 | y1 := yy & 255 226 | y0 := 256 - y1 227 | 228 | // Apply weighted color to pixel. 229 | // w0 is color weight, w1 is existing weight. 230 | // Sum of w0 and w1 must be <= 256 231 | weight := func(pix *byte, w0, w1 int) { 232 | p := int(*pix)*w1 + int(col)*w0 233 | *pix = byte(p >> 8) 234 | } 235 | 236 | // Remove fraction from pixels coordinates. 237 | xx >>= 8 238 | yy >>= 8 239 | 240 | // Check if we can write to the next pixel 241 | yOK := yy < img.Rect.Dy()-1 242 | 243 | // Pre-multiply with image stride 244 | yy *= img.Stride 245 | 246 | // draw topleft pixel. 247 | p := &img.Pix[xx+yy] 248 | weight(p, y0, y1) 249 | if yOK { 250 | yy += img.Stride 251 | p = &img.Pix[xx+yy] 252 | weight(p, y1, y0) 253 | } 254 | } 255 | 256 | // roundP will round positive numbers towards nearest integer. 257 | func roundP(x float32) int { 258 | return int(x + 0.5) 259 | } 260 | 261 | func absf(x float32) float32 { 262 | switch { 263 | case x < 0: 264 | return -x 265 | case x == 0: 266 | return 0 // return correctly abs(-0) 267 | } 268 | return x 269 | } 270 | 271 | // line clipping converted from C++ on 272 | // https://www.geeksforgeeks.org/line-clipping-set-1-cohen-sutherland-algorithm/ 273 | // Will also ensure that Y1 is smaller or equal to Y2. 274 | func (l *Line) clip(w, h int) bool { 275 | // Defining region codes 276 | const ( 277 | INSIDE = 0 278 | LEFT = 1 << iota 279 | RIGHT 280 | BOTTOM 281 | TOP 282 | ) 283 | if l.P1.Y > l.P2.Y { 284 | // Swap 285 | l.P1.X, l.P2.X = l.P2.X, l.P1.X 286 | l.P1.Y, l.P2.Y = l.P2.Y, l.P1.Y 287 | } 288 | fw, fh := float32(w)-0.51, float32(h)-0.51 289 | 290 | // Function to compute region code for a point(X, y) 291 | var computeCode = func(p Point2D) int { 292 | // initialized as being inside 293 | code := INSIDE 294 | 295 | if p.X < 0 { 296 | // to the left of rectangle 297 | code |= LEFT 298 | } else if p.X > fw { 299 | // to the right of rectangle 300 | code |= RIGHT 301 | } 302 | if p.Y < 0 { 303 | // below the rectangle 304 | code |= BOTTOM 305 | } else if p.Y > fh { 306 | // above the rectangle 307 | code |= TOP 308 | } 309 | return code 310 | } 311 | 312 | code1 := computeCode(l.P1) 313 | code2 := computeCode(l.P2) 314 | const minLen = (0.1 * 0.1) * 2 315 | 316 | for n := 0; ; n++ { 317 | if false && n > 5 { 318 | code1 = computeCode(l.P1) 319 | code2 = computeCode(l.P2) 320 | fmt.Println("Could not clip", l.P1, code1, "->", l.P2, code2) 321 | return false 322 | } 323 | if l.P1.DistSq(l.P2) < minLen { 324 | // Line too small to draw. 325 | return false 326 | } 327 | if (code1 == 0) && (code2 == 0) { 328 | // If both endpoints lie within rectangle 329 | return true 330 | } 331 | if (code1 & code2) != 0 { 332 | // If both endpoints are outside rectangle, 333 | // in same region 334 | return false 335 | } 336 | 337 | // Some segment of line lies within the 338 | // rectangle 339 | var codeOut int 340 | var x, y float32 341 | 342 | // At least one endpoint is outside the 343 | // rectangle, pick it. 344 | if code1 != 0 { 345 | codeOut = code1 346 | } else { 347 | codeOut = code2 348 | } 349 | 350 | // Find intersection point; 351 | // using formulas y = Y1 + slope * (X - X1), 352 | // X = X1 + (1 / slope) * (y - Y1) 353 | switch { 354 | case (codeOut & TOP) != 0: 355 | // point is above the clip rectangle 356 | x = l.P1.X + (l.P2.X-l.P1.X)*(fh-l.P1.Y)/(l.P2.Y-l.P1.Y) 357 | y = fh 358 | 359 | case (codeOut & BOTTOM) != 0: 360 | // point is below the rectangle 361 | x = l.P1.X + (l.P2.X-l.P1.X)*(-l.P1.Y)/(l.P2.Y-l.P1.Y) 362 | y = 0 363 | case (codeOut & RIGHT) != 0: 364 | // point is to the right of rectangle 365 | y = l.P1.Y + (l.P2.Y-l.P1.Y)*(fw-l.P1.X)/(l.P2.X-l.P1.X) 366 | x = fw 367 | case (codeOut & LEFT) != 0: 368 | // point is to the left of rectangle 369 | y = l.P1.Y + (l.P2.Y-l.P1.Y)*(-l.P1.X)/(l.P2.X-l.P1.X) 370 | x = 0 371 | } 372 | 373 | // Now intersection point X,y is found 374 | // We replace point outside rectangle 375 | // by intersection point 376 | if codeOut == code1 { 377 | l.P1.X = x 378 | l.P1.Y = y 379 | code1 = computeCode(l.P1) 380 | } else { 381 | l.P2.X = x 382 | l.P2.Y = y 383 | code2 = computeCode(l.P2) 384 | } 385 | } 386 | } 387 | -------------------------------------------------------------------------------- /hoaxplus/primitive/objloader.go: -------------------------------------------------------------------------------- 1 | package primitive 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "fmt" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | // LoadOBJ will load and OBJ and return vertices and deduplicated edges. 12 | func LoadOBJ(b []byte) (verts P3Ds, edges [][2]int) { 13 | scanner := bufio.NewScanner(bytes.NewBuffer(b)) 14 | known := make(map[uint64]struct{}) 15 | 16 | addLine := func(a, b uint64) { 17 | if a == b { 18 | return 19 | } 20 | if b < a { 21 | a, b = b, a 22 | } 23 | v := a | (b << 32) 24 | if _, ok := known[v]; ok { 25 | return 26 | } 27 | known[v] = struct{}{} 28 | edges = append(edges, [2]int{int(a), int(b)}) 29 | } 30 | 31 | for scanner.Scan() { 32 | t := scanner.Text() 33 | if strings.HasPrefix(t, "v ") { 34 | var c Point3D 35 | n, err := fmt.Sscanf(t, "v %f %f %f", &c.X, &c.Y, &c.Z) 36 | if err != nil { 37 | panic(err) 38 | } 39 | if n != 3 { 40 | panic("not 3") 41 | } 42 | verts = append(verts, c) 43 | continue 44 | } 45 | if strings.HasPrefix(t, "f ") { 46 | t := strings.TrimPrefix(t, "f ") 47 | faces := strings.Fields(t) 48 | indices := make([]uint64, len(faces)) 49 | for i, v := range faces { 50 | s, err := strconv.ParseUint(v, 10, 32) 51 | if err != nil { 52 | panic(err) 53 | } 54 | if s == 0 { 55 | panic("index 0 face found, should start with 1") 56 | } 57 | indices[i] = s - 1 58 | } 59 | for i, v := range indices { 60 | if i == 0 { 61 | // add first to last 62 | addLine(v, indices[len(indices)-1]) 63 | continue 64 | } 65 | addLine(v, indices[i-1]) 66 | } 67 | } 68 | } 69 | return 70 | } 71 | -------------------------------------------------------------------------------- /hoaxplus/primitive/point2d.go: -------------------------------------------------------------------------------- 1 | package primitive 2 | 3 | type Point2D struct { 4 | X, Y float32 5 | } 6 | 7 | type P2Ds []Point2D 8 | 9 | func (p Point2D) DistSq(p2 Point2D) float32 { 10 | a, b := p.X-p2.X, p.Y-p2.Y 11 | return a*a + b*b 12 | } 13 | -------------------------------------------------------------------------------- /hoaxplus/primitive/point3d.go: -------------------------------------------------------------------------------- 1 | package primitive 2 | 3 | import "math" 4 | 5 | type Point3D struct{ X, Y, Z float32 } 6 | type P3Ds []Point3D 7 | 8 | func (c *Point3D) Scale(f float32) { 9 | c.X *= f 10 | c.Y *= f 11 | c.Z *= f 12 | } 13 | 14 | func (p P3Ds) Scale(f float32) { 15 | for i := range p { 16 | p[i].Scale(f) 17 | } 18 | } 19 | 20 | // rotateFn returns a function that rotates around (0,0,0). 21 | // Supply angles in radians. 22 | // dst must be same size or bigger than p. 23 | func (p P3Ds) RotateTo(dst P3Ds, xAn, yAn, zAn float64) { 24 | fn := rotateFn(xAn, yAn, zAn) 25 | for i, v := range p { 26 | dst[i] = fn(v) 27 | } 28 | } 29 | 30 | // BehindCamera is magic 31 | const BehindCamera = 10e21 32 | 33 | // rotateFn returns a function that rotates around (0,0,0). 34 | // dst must be same size or bigger than p. 35 | func (p P3Ds) ProjectTo(dst []Point2D, w, h, zoff float32) { 36 | halfWidth := w * 0.5 37 | halfHeight := h * 0.5 38 | for i, v := range p { 39 | z := v.Z + zoff 40 | if z <= 0 { 41 | dst[i] = Point2D{X: BehindCamera, Y: BehindCamera} 42 | continue 43 | } 44 | invZ := 1 / z 45 | x := halfWidth * v.X * invZ 46 | y := halfWidth * v.Y * invZ 47 | x += halfWidth 48 | y += halfHeight 49 | dst[i] = Point2D{X: x, Y: y} 50 | } 51 | } 52 | 53 | // rotateFn returns a function that rotates around (0,0,0). 54 | // Supply angles in radians. 55 | func rotateFn(xAn, yAn, zAn float64) func(c Point3D) Point3D { 56 | var ( 57 | s1 = float32(math.Sin(zAn)) 58 | s2 = float32(math.Sin(xAn)) 59 | s3 = float32(math.Sin(yAn)) 60 | c1 = float32(math.Cos(zAn)) 61 | c2 = float32(math.Cos(xAn)) 62 | c3 = float32(math.Cos(yAn)) 63 | 64 | zero = c1*c3 + s1*s2*s3 65 | one = c2 * s3 66 | two = -c3*s1 + c1*s2*s3 67 | three = c2 * s1 68 | four = -s2 69 | five = c2 * c1 70 | six = -c1*s3 + c3*s1*s2 71 | seven = c2 * c3 72 | eight = s1*s3 + c1*c3*s2 73 | ) 74 | return func(c Point3D) Point3D { 75 | c.X, c.Y, c.Z = c.X*zero+c.Y*one+c.Z*two, c.X*three+c.Y*four+c.Z*five, c.X*six+c.Y*seven+c.Z*eight 76 | return c 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Go After Dark 7 | 8 | 9 | 10 | 11 |

GO AFTER DARK

12 |

13 | Go After Dark is a video series where we create realtime graphical effects in Go. 14 | We don't care about boring everyday problems but try to make something cool looking and fun. 15 | This series allows you to pick up graphical programming from the start - only assuming you have basic knowledge of the language. 16 |

17 | 18 |

Episode list

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 |
TeaserWelcome to Go After DarkSee teaser in browserWatch teaser on Youtube
Episode 1Introduction and a basic effectSee effect in browserWatch Episode on Youtube
Episode 2Tiling Zoomer and Fixed Point MathSee effect in browserWatch Episode on Youtube
Episode 3Zoom, Rotate, Enhance! (Rotozoomer)See effect in browserWatch Episode on Youtube
Episode 4Ride the tunnel (Fake 3D tunnel)See effect in browserWatch Episode on Youtube
Episode 5It’s 3D, baby! (Basic 3D software rendering))See effect in browserWatch Episode on Youtube
Episode 6Let it Show! (Scaled 2D sprites))See effect in browserWatch Episode on Youtube
Season 2
Episode 7It's a Hoax! (64KB Intro)Code from episodeWatch Episode on Youtube
Episode 8Walk the Line! (Draw Antialiased Lines)See effect in browserWatch Episode on Youtube
78 |

Resources

79 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /loader.js: -------------------------------------------------------------------------------- 1 | var script = document.createElement('script'); 2 | script.src = "./../wasm_exec.js"; 3 | 4 | script.onload = function () { 5 | if (!WebAssembly.instantiateStreaming) { // polyfill 6 | WebAssembly.instantiateStreaming = async (resp, importObject) => { 7 | const source = await (await resp).arrayBuffer(); 8 | return await WebAssembly.instantiate(source, importObject); 9 | }; 10 | } 11 | 12 | const go = new Go(); 13 | let mod, inst; 14 | WebAssembly.instantiateStreaming(fetch("./fx.wasm"), go.importObject).then((result) => { 15 | mod = result.module; 16 | inst = result.instance; 17 | run(); 18 | }).catch(reason => { 19 | var status = document.querySelector('#status'); 20 | status.innerHTML = reason; 21 | }); 22 | 23 | async function run() { 24 | console.clear(); 25 | await go.run(inst); 26 | inst = await WebAssembly.instantiate(mod, go.importObject); // reset instance 27 | } 28 | 29 | var canvas = document.querySelector('#canvas-container'); 30 | 31 | canvas.requestFullScreen = canvas.webkitRequestFullScreen || canvas.msRequestFullscreen || 32 | canvas.mozRequestFullScreen || canvas.requestFullScreen; 33 | 34 | canvas.cancelFullScreen = canvas.webkitCancelFullScreen || canvas.msCancelFullscreen || 35 | canvas.mozCancelFullScreen || canvas.cancelFullScreen; 36 | 37 | }; 38 | document.head.appendChild(script); 39 | 40 | -------------------------------------------------------------------------------- /server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | "os" 8 | "path/filepath" 9 | ) 10 | 11 | func main() { 12 | dir, _ := os.Getwd() 13 | if !filepath.IsAbs(dir) { 14 | dir, _ = filepath.Abs(dir) 15 | } 16 | dir = filepath.Clean(dir) 17 | fmt.Println("Serving from:", dir, "on http://localhost:8080") 18 | http.Handle("/", http.FileServer(http.Dir(dir))) 19 | log.Print(http.ListenAndServe(":8080", nil)) 20 | } 21 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | #canvas-container { 2 | width: 1280px; 3 | height: 720px; 4 | text-align: center; 5 | border: 1px solid black; 6 | } 7 | 8 | #status { 9 | font-family: 'Lato', sans-serif; 10 | color: #555; 11 | } 12 | 13 | #fullscreen { 14 | color: whitesmoke; 15 | font-family: 'Lato', sans-serif; 16 | padding: 5px; 17 | border-color: #555; 18 | border-style: dashed; 19 | border-width: 1px; 20 | text-align: center; 21 | font-size: 18px; 22 | } 23 | 24 | #fullscreen svg { 25 | fill: currentColor; 26 | display: inline; 27 | margin: auto; 28 | } 29 | 30 | body { 31 | color: whitesmoke; 32 | width: 1280px; 33 | margin: auto; 34 | background: #111; 35 | font-family: 'Lato', sans-serif; 36 | } 37 | 38 | canvas { 39 | background: #000; 40 | image-rendering: -moz-crisp-edges; /* Firefox */ 41 | image-rendering: -webkit-crisp-edges; /* Webkit (Safari) */ 42 | image-rendering: pixelated; /* Chrome */ 43 | transform: scale(2); 44 | transform-origin: top center; 45 | display:inline; 46 | } 47 | 48 | .status { 49 | background: #111; 50 | align-self: center; 51 | width: 1280px; 52 | } 53 | 54 | a:link { 55 | color: white; 56 | } 57 | 58 | /* visited link */ 59 | a:visited { 60 | color: white; 61 | } 62 | 63 | /* mouse over link */ 64 | a:hover { 65 | color: orange; 66 | } 67 | 68 | /* selected link */ 69 | a:active { 70 | color: grey; 71 | } 72 | 73 | table { 74 | border-width: 1px; 75 | border-style: dotted; 76 | border-collapse: collapse; 77 | border-color: #333; 78 | width: 80%; 79 | padding: 15px; 80 | } 81 | 82 | td { 83 | text-align: left; 84 | padding: 15px; 85 | } 86 | 87 | h1 { 88 | color: orange; 89 | } 90 | -------------------------------------------------------------------------------- /wasm_exec.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | (() => { 6 | // Map web browser API and Node.js API to a single common API (preferring web standards over Node.js API). 7 | const isNodeJS = typeof process !== "undefined"; 8 | if (isNodeJS) { 9 | global.require = require; 10 | global.fs = require("fs"); 11 | 12 | const nodeCrypto = require("crypto"); 13 | global.crypto = { 14 | getRandomValues(b) { 15 | nodeCrypto.randomFillSync(b); 16 | }, 17 | }; 18 | 19 | global.performance = { 20 | now() { 21 | const [sec, nsec] = process.hrtime(); 22 | return sec * 1000 + nsec / 1000000; 23 | }, 24 | }; 25 | 26 | const util = require("util"); 27 | global.TextEncoder = util.TextEncoder; 28 | global.TextDecoder = util.TextDecoder; 29 | } else { 30 | if (typeof window !== "undefined") { 31 | window.global = window; 32 | } else if (typeof self !== "undefined") { 33 | self.global = self; 34 | } else { 35 | throw new Error("cannot export Go (neither window nor self is defined)"); 36 | } 37 | 38 | let outputBuf = ""; 39 | global.fs = { 40 | constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused 41 | writeSync(fd, buf) { 42 | outputBuf += decoder.decode(buf); 43 | const nl = outputBuf.lastIndexOf("\n"); 44 | if (nl != -1) { 45 | console.log(outputBuf.substr(0, nl)); 46 | outputBuf = outputBuf.substr(nl + 1); 47 | } 48 | return buf.length; 49 | }, 50 | openSync(path, flags, mode) { 51 | const err = new Error("not implemented"); 52 | err.code = "ENOSYS"; 53 | throw err; 54 | }, 55 | }; 56 | } 57 | 58 | const encoder = new TextEncoder("utf-8"); 59 | const decoder = new TextDecoder("utf-8"); 60 | 61 | global.Go = class { 62 | constructor() { 63 | this.argv = ["js"]; 64 | this.env = {}; 65 | this.exit = (code) => { 66 | if (code !== 0) { 67 | console.warn("exit code:", code); 68 | } 69 | }; 70 | this._callbackTimeouts = new Map(); 71 | this._nextCallbackTimeoutID = 1; 72 | 73 | const mem = () => { 74 | // The buffer may change when requesting more memory. 75 | return new DataView(this._inst.exports.mem.buffer); 76 | } 77 | 78 | const setInt64 = (addr, v) => { 79 | mem().setUint32(addr + 0, v, true); 80 | mem().setUint32(addr + 4, Math.floor(v / 4294967296), true); 81 | } 82 | 83 | const getInt64 = (addr) => { 84 | const low = mem().getUint32(addr + 0, true); 85 | const high = mem().getInt32(addr + 4, true); 86 | return low + high * 4294967296; 87 | } 88 | 89 | const loadValue = (addr) => { 90 | const f = mem().getFloat64(addr, true); 91 | if (!isNaN(f)) { 92 | return f; 93 | } 94 | 95 | const id = mem().getUint32(addr, true); 96 | return this._values[id]; 97 | } 98 | 99 | const storeValue = (addr, v) => { 100 | const nanHead = 0x7FF80000; 101 | 102 | if (typeof v === "number") { 103 | if (isNaN(v)) { 104 | mem().setUint32(addr + 4, nanHead, true); 105 | mem().setUint32(addr, 0, true); 106 | return; 107 | } 108 | mem().setFloat64(addr, v, true); 109 | return; 110 | } 111 | 112 | switch (v) { 113 | case undefined: 114 | mem().setUint32(addr + 4, nanHead, true); 115 | mem().setUint32(addr, 1, true); 116 | return; 117 | case null: 118 | mem().setUint32(addr + 4, nanHead, true); 119 | mem().setUint32(addr, 2, true); 120 | return; 121 | case true: 122 | mem().setUint32(addr + 4, nanHead, true); 123 | mem().setUint32(addr, 3, true); 124 | return; 125 | case false: 126 | mem().setUint32(addr + 4, nanHead, true); 127 | mem().setUint32(addr, 4, true); 128 | return; 129 | } 130 | 131 | let ref = this._refs.get(v); 132 | if (ref === undefined) { 133 | ref = this._values.length; 134 | this._values.push(v); 135 | this._refs.set(v, ref); 136 | } 137 | let typeFlag = 0; 138 | switch (typeof v) { 139 | case "string": 140 | typeFlag = 1; 141 | break; 142 | case "symbol": 143 | typeFlag = 2; 144 | break; 145 | case "function": 146 | typeFlag = 3; 147 | break; 148 | } 149 | mem().setUint32(addr + 4, nanHead | typeFlag, true); 150 | mem().setUint32(addr, ref, true); 151 | } 152 | 153 | const loadSlice = (addr) => { 154 | const array = getInt64(addr + 0); 155 | const len = getInt64(addr + 8); 156 | return new Uint8Array(this._inst.exports.mem.buffer, array, len); 157 | } 158 | 159 | const loadSliceOfValues = (addr) => { 160 | const array = getInt64(addr + 0); 161 | const len = getInt64(addr + 8); 162 | const a = new Array(len); 163 | for (let i = 0; i < len; i++) { 164 | a[i] = loadValue(array + i * 8); 165 | } 166 | return a; 167 | } 168 | 169 | const loadString = (addr) => { 170 | const saddr = getInt64(addr + 0); 171 | const len = getInt64(addr + 8); 172 | return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len)); 173 | } 174 | 175 | const timeOrigin = Date.now() - performance.now(); 176 | this.importObject = { 177 | go: { 178 | // func wasmExit(code int32) 179 | "runtime.wasmExit": (sp) => { 180 | const code = mem().getInt32(sp + 8, true); 181 | this.exited = true; 182 | delete this._inst; 183 | delete this._values; 184 | delete this._refs; 185 | this.exit(code); 186 | }, 187 | 188 | // func wasmWrite(fd uintptr, p unsafe.Pointer, n int32) 189 | "runtime.wasmWrite": (sp) => { 190 | const fd = getInt64(sp + 8); 191 | const p = getInt64(sp + 16); 192 | const n = mem().getInt32(sp + 24, true); 193 | fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n)); 194 | }, 195 | 196 | // func nanotime() int64 197 | "runtime.nanotime": (sp) => { 198 | setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000); 199 | }, 200 | 201 | // func walltime() (sec int64, nsec int32) 202 | "runtime.walltime": (sp) => { 203 | const msec = (new Date).getTime(); 204 | setInt64(sp + 8, msec / 1000); 205 | mem().setInt32(sp + 16, (msec % 1000) * 1000000, true); 206 | }, 207 | 208 | // func scheduleCallback(delay int64) int32 209 | "runtime.scheduleCallback": (sp) => { 210 | const id = this._nextCallbackTimeoutID; 211 | this._nextCallbackTimeoutID++; 212 | this._callbackTimeouts.set(id, setTimeout( 213 | () => { this._resolveCallbackPromise(); }, 214 | getInt64(sp + 8) + 1, // setTimeout has been seen to fire up to 1 millisecond early 215 | )); 216 | mem().setInt32(sp + 16, id, true); 217 | }, 218 | 219 | // func clearScheduledCallback(id int32) 220 | "runtime.clearScheduledCallback": (sp) => { 221 | const id = mem().getInt32(sp + 8, true); 222 | clearTimeout(this._callbackTimeouts.get(id)); 223 | this._callbackTimeouts.delete(id); 224 | }, 225 | 226 | // func getRandomData(r []byte) 227 | "runtime.getRandomData": (sp) => { 228 | crypto.getRandomValues(loadSlice(sp + 8)); 229 | }, 230 | 231 | // func stringVal(value string) ref 232 | "syscall/js.stringVal": (sp) => { 233 | storeValue(sp + 24, loadString(sp + 8)); 234 | }, 235 | 236 | // func valueGet(v ref, p string) ref 237 | "syscall/js.valueGet": (sp) => { 238 | storeValue(sp + 32, Reflect.get(loadValue(sp + 8), loadString(sp + 16))); 239 | }, 240 | 241 | // func valueSet(v ref, p string, x ref) 242 | "syscall/js.valueSet": (sp) => { 243 | Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32)); 244 | }, 245 | 246 | // func valueIndex(v ref, i int) ref 247 | "syscall/js.valueIndex": (sp) => { 248 | storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16))); 249 | }, 250 | 251 | // valueSetIndex(v ref, i int, x ref) 252 | "syscall/js.valueSetIndex": (sp) => { 253 | Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24)); 254 | }, 255 | 256 | // func valueCall(v ref, m string, args []ref) (ref, bool) 257 | "syscall/js.valueCall": (sp) => { 258 | try { 259 | const v = loadValue(sp + 8); 260 | const m = Reflect.get(v, loadString(sp + 16)); 261 | const args = loadSliceOfValues(sp + 32); 262 | storeValue(sp + 56, Reflect.apply(m, v, args)); 263 | mem().setUint8(sp + 64, 1); 264 | } catch (err) { 265 | storeValue(sp + 56, err); 266 | mem().setUint8(sp + 64, 0); 267 | } 268 | }, 269 | 270 | // func valueInvoke(v ref, args []ref) (ref, bool) 271 | "syscall/js.valueInvoke": (sp) => { 272 | try { 273 | const v = loadValue(sp + 8); 274 | const args = loadSliceOfValues(sp + 16); 275 | storeValue(sp + 40, Reflect.apply(v, undefined, args)); 276 | mem().setUint8(sp + 48, 1); 277 | } catch (err) { 278 | storeValue(sp + 40, err); 279 | mem().setUint8(sp + 48, 0); 280 | } 281 | }, 282 | 283 | // func valueNew(v ref, args []ref) (ref, bool) 284 | "syscall/js.valueNew": (sp) => { 285 | try { 286 | const v = loadValue(sp + 8); 287 | const args = loadSliceOfValues(sp + 16); 288 | storeValue(sp + 40, Reflect.construct(v, args)); 289 | mem().setUint8(sp + 48, 1); 290 | } catch (err) { 291 | storeValue(sp + 40, err); 292 | mem().setUint8(sp + 48, 0); 293 | } 294 | }, 295 | 296 | // func valueLength(v ref) int 297 | "syscall/js.valueLength": (sp) => { 298 | setInt64(sp + 16, parseInt(loadValue(sp + 8).length)); 299 | }, 300 | 301 | // valuePrepareString(v ref) (ref, int) 302 | "syscall/js.valuePrepareString": (sp) => { 303 | const str = encoder.encode(String(loadValue(sp + 8))); 304 | storeValue(sp + 16, str); 305 | setInt64(sp + 24, str.length); 306 | }, 307 | 308 | // valueLoadString(v ref, b []byte) 309 | "syscall/js.valueLoadString": (sp) => { 310 | const str = loadValue(sp + 8); 311 | loadSlice(sp + 16).set(str); 312 | }, 313 | 314 | // func valueInstanceOf(v ref, t ref) bool 315 | "syscall/js.valueInstanceOf": (sp) => { 316 | mem().setUint8(sp + 24, loadValue(sp + 8) instanceof loadValue(sp + 16)); 317 | }, 318 | 319 | "debug": (value) => { 320 | console.log(value); 321 | }, 322 | } 323 | }; 324 | } 325 | 326 | async run(instance) { 327 | this._inst = instance; 328 | this._values = [ // TODO: garbage collection 329 | NaN, 330 | undefined, 331 | null, 332 | true, 333 | false, 334 | global, 335 | this._inst.exports.mem, 336 | this, 337 | ]; 338 | this._refs = new Map(); 339 | this._callbackShutdown = false; 340 | this.exited = false; 341 | 342 | const mem = new DataView(this._inst.exports.mem.buffer) 343 | 344 | // Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory. 345 | let offset = 4096; 346 | 347 | const strPtr = (str) => { 348 | let ptr = offset; 349 | new Uint8Array(mem.buffer, offset, str.length + 1).set(encoder.encode(str + "\0")); 350 | offset += str.length + (8 - (str.length % 8)); 351 | return ptr; 352 | }; 353 | 354 | const argc = this.argv.length; 355 | 356 | const argvPtrs = []; 357 | this.argv.forEach((arg) => { 358 | argvPtrs.push(strPtr(arg)); 359 | }); 360 | 361 | const keys = Object.keys(this.env).sort(); 362 | argvPtrs.push(keys.length); 363 | keys.forEach((key) => { 364 | argvPtrs.push(strPtr(`${key}=${this.env[key]}`)); 365 | }); 366 | 367 | const argv = offset; 368 | argvPtrs.forEach((ptr) => { 369 | mem.setUint32(offset, ptr, true); 370 | mem.setUint32(offset + 4, 0, true); 371 | offset += 8; 372 | }); 373 | 374 | while (true) { 375 | const callbackPromise = new Promise((resolve) => { 376 | this._resolveCallbackPromise = () => { 377 | if (this.exited) { 378 | throw new Error("bad callback: Go program has already exited"); 379 | } 380 | setTimeout(resolve, 0); // make sure it is asynchronous 381 | }; 382 | }); 383 | this._inst.exports.run(argc, argv); 384 | if (this.exited) { 385 | break; 386 | } 387 | await callbackPromise; 388 | } 389 | } 390 | 391 | static _makeCallbackHelper(id, pendingCallbacks, go) { 392 | return function() { 393 | pendingCallbacks.push({ id: id, args: arguments }); 394 | go._resolveCallbackPromise(); 395 | }; 396 | } 397 | 398 | static _makeEventCallbackHelper(preventDefault, stopPropagation, stopImmediatePropagation, fn) { 399 | return function(event) { 400 | if (preventDefault) { 401 | event.preventDefault(); 402 | } 403 | if (stopPropagation) { 404 | event.stopPropagation(); 405 | } 406 | if (stopImmediatePropagation) { 407 | event.stopImmediatePropagation(); 408 | } 409 | fn(event); 410 | }; 411 | } 412 | } 413 | 414 | if (isNodeJS) { 415 | if (process.argv.length < 3) { 416 | process.stderr.write("usage: go_js_wasm_exec [wasm binary] [arguments]\n"); 417 | process.exit(1); 418 | } 419 | 420 | const go = new Go(); 421 | go.argv = process.argv.slice(2); 422 | go.env = process.env; 423 | go.exit = process.exit; 424 | WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => { 425 | process.on("exit", (code) => { // Node.js exits if no callback is pending 426 | if (code === 0 && !go.exited) { 427 | // deadlock, make Go print error and stack traces 428 | go._callbackShutdown = true; 429 | go._inst.exports.run(); 430 | } 431 | }); 432 | return go.run(result.instance); 433 | }).catch((err) => { 434 | throw err; 435 | }); 436 | } 437 | })(); 438 | --------------------------------------------------------------------------------