/qcs/qcs.go

https://code.google.com/p/goxscr/ · Go · 241 lines · 195 code · 34 blank · 12 comment · 32 complexity · e108646cc14f4c7bc2309e19306a4e91 MD5 · raw file

  1. // QuasiCrystals
  2. package main
  3. import (
  4. "code.google.com/p/x-go-binding/ui"
  5. "code.google.com/p/x-go-binding/ui/x11"
  6. "flag"
  7. "fmt"
  8. "image"
  9. "image/color"
  10. "image/draw"
  11. "math"
  12. "math/rand"
  13. "os"
  14. "runtime"
  15. "time"
  16. )
  17. const Degree = math.Pi / 180
  18. var palette = make([]image.Image, 256)
  19. type point struct {
  20. x, y float64
  21. }
  22. var workers = flag.Int("w", 3, "workers")
  23. var frames = flag.Int64("f", 30, "max framerate")
  24. var randomize = flag.Bool("r", false, "randomize size, scale, degree and phi")
  25. var phi = flag.Float64("phi", 5, "step phase change")
  26. var size = flag.Int("size", 300, "crystal size")
  27. var scale = flag.Float64("scale", 30, "scale")
  28. var degree = flag.Int("degree", 5, "degree")
  29. type Work struct {
  30. e int // frame number to compute.
  31. img draw.Image // image to write to.
  32. done chan bool // send on this channel when work is done.
  33. }
  34. func init() {
  35. for i := range palette {
  36. palette[i] = image.NewUniform(color.Gray{byte(i)})
  37. }
  38. }
  39. func pt(x, y int) point {
  40. denom := float64(*size) - 1
  41. X := *scale * ((float64(2*x) / denom) - 1)
  42. Y := *scale * ((float64(2*y) / denom) - 1)
  43. return point{X, Y}
  44. }
  45. func transform(θ float64, p point) point {
  46. sin, cos := math.Sincos(θ)
  47. p.x = p.x*cos - p.y*sin
  48. p.y = p.x*sin + p.y*cos
  49. return p
  50. }
  51. func worker(wc <-chan *Work) {
  52. buf := make([]byte, *size**size)
  53. sz := *size
  54. for w := range wc {
  55. r := w.img.Bounds()
  56. dx := r.Dx()
  57. dy := r.Dy()
  58. stridex := 1 + dx/sz // how big is each pixel from our crystal
  59. stridey := 1 + dy/sz
  60. ϕ := float64(w.e) * (*phi) * Degree
  61. quasicrystal(sz, *degree, ϕ, buf)
  62. for y := 0; y < sz; y++ {
  63. if y*stridey > dy {
  64. break
  65. }
  66. for x := 0; x < sz; x++ {
  67. if x*stridex > dx {
  68. break
  69. }
  70. nr := image.Rect(x*stridex, y*stridey, x*stridex+stridex, y*stridey+stridey)
  71. draw.Draw(w.img, nr, palette[buf[y*sz+x]], image.ZP, draw.Src)
  72. }
  73. }
  74. w.done <- true
  75. }
  76. }
  77. func main() {
  78. flag.Parse()
  79. runtime.GOMAXPROCS(*workers + 1)
  80. rand.Seed(time.Now().UnixNano())
  81. if *randomize {
  82. *phi = rand.Float64() * 10
  83. *size = 100 + rand.Intn(200)
  84. *scale = 25 + rand.Float64()*10
  85. *degree = 3 + rand.Intn(5)
  86. }
  87. window, err := x11.NewWindow()
  88. if err != nil {
  89. fmt.Fprintf(os.Stderr, "error:", err.Error())
  90. return
  91. }
  92. quit := make(chan chan<- stats)
  93. go painter(window, quit)
  94. loop:
  95. for e := range window.EventChan() {
  96. switch f := e.(type) {
  97. case ui.MouseEvent:
  98. case ui.KeyEvent:
  99. if f.Key == 65307 { // ESC
  100. break loop
  101. }
  102. case ui.ConfigEvent:
  103. // nothing for now
  104. case ui.ErrEvent:
  105. break loop
  106. }
  107. }
  108. c := make(chan stats)
  109. quit <- c
  110. st := <-c
  111. fmt.Printf("fps: %.1f, spf %.0fms, dev %.0fms\n", 1e9/st.mean, st.mean/1e6, st.stddev/1e6)
  112. }
  113. type stats struct {
  114. mean float64
  115. stddev float64
  116. }
  117. func painter(win ui.Window, quit <-chan chan<- stats) {
  118. ticker := time.NewTicker(1e9 / time.Duration(*frames))
  119. screen := win.Screen()
  120. r := screen.Bounds()
  121. // make more work items than workers so that we
  122. // can keep a worker busy even when the last frame
  123. // that it has computed has not yet been retrieved by the
  124. // painter loop.
  125. work := make([]Work, *workers*2)
  126. workChan := make(chan *Work)
  127. for i := 0; i < *workers; i++ {
  128. go worker(workChan)
  129. }
  130. e := 0
  131. frames := 0
  132. now := time.Now()
  133. start := now
  134. var sumdt2 float64
  135. // continuously cycle through the array of work items,
  136. // waiting for each to be done in turn.
  137. for {
  138. for i := range work {
  139. w := &work[i]
  140. if w.img == nil {
  141. // If this is the first time we've used a work item, so make the image
  142. // and the done channel. There's no image calculated yet.
  143. w.img = image.NewRGBA(screen.Bounds())
  144. w.done = make(chan bool, 1)
  145. } else {
  146. <-w.done
  147. draw.Draw(screen, r, w.img, image.ZP, draw.Src)
  148. win.FlushImage()
  149. frames++
  150. // wait for the next tick event or to be asked to quit.
  151. select {
  152. case t := <-ticker.C:
  153. dt := t.Sub(now)
  154. sumdt2 += float64(dt * dt)
  155. now = t
  156. case c := <-quit:
  157. mean := float64((now.Sub(start)) / time.Duration(frames))
  158. c <- stats{
  159. mean: mean,
  160. stddev: math.Sqrt((sumdt2 / float64(frames)) - mean*mean),
  161. }
  162. return
  163. }
  164. }
  165. // start the new work item running on any worker that's available.
  166. w.e = e
  167. e++
  168. workChan <- w
  169. }
  170. }
  171. }
  172. func wave(ϕ, θ float64, p point) float64 {
  173. sin, cos := math.Sincos(θ)
  174. return (math.Cos(cos*p.x+sin*p.y+ϕ) + 1.0) / 2.0
  175. }
  176. func wave1(ϕ, θ float64, p point) float64 {
  177. if θ != 0.0 {
  178. p = transform(θ, p)
  179. }
  180. sin, cos := math.Sincos(ϕ)
  181. return (math.Cos(cos*p.x+sin*p.y) + 1.0) / 2.0
  182. }
  183. func wave2(ϕ, θ float64, p point) float64 {
  184. if θ != 0.0 {
  185. p = transform(θ, p)
  186. }
  187. return (math.Cos(ϕ+p.y) + 1.) / 2.0
  188. }
  189. func quasicrystal(size, degree int, ϕ float64, buf []byte) {
  190. for y := 0; y < size; y++ {
  191. for x := 0; x < size; x++ {
  192. θ := 0 * Degree
  193. p := pt(x, y)
  194. acc := wave(ϕ, θ, p)
  195. for d := 1; d < degree; d++ {
  196. θ += 180 * Degree / float64(degree)
  197. if d%2 == 1 {
  198. acc += 1 - wave(ϕ, θ, p)
  199. } else {
  200. acc += wave(ϕ, θ, p)
  201. }
  202. }
  203. buf[y*size+x] = byte(acc * 255.0)
  204. }
  205. }
  206. return
  207. }