spinrite.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. package door
  2. import (
  3. "bytes"
  4. )
  5. type SpinRite struct {
  6. Width uint8 // Width of the animation (must be odd)
  7. Length uint8 // Width of the worm (must be odd)
  8. Color []byte // Color (default CYAN ON BLUE)
  9. OutputB []byte // Output CP437/bytes (Width)
  10. OutputR []rune // Output Unicode (Width)
  11. Buffer []uint8 // Output calculate buffer (Width)
  12. Runes []rune // blank, center, top, bottom, both
  13. Bytes []byte // blank, center, top, bottom, both
  14. CenterPos uint8 // Center Position (Width - 1) / 2
  15. StartPos uint8 // Starting Position (Length -1) / 2
  16. Index uint8 // Index of current iteration
  17. output *bytes.Buffer // Buffer for output
  18. }
  19. /*
  20. Other center character options: · ∞
  21. We default to ∞
  22. [ █████ ]
  23. [ ▄▄███▀▀ ]
  24. [ ▄▄▄▄█▀▀▀▀ ]
  25. [ ▄▄▄▄▄•▀▀▀▀▀ ]
  26. [▄▄▄▄▄ • ▀▀▀▀▀]
  27. [█▄▄▄ • ▀▀▀█]
  28. [██▄ • ▀██]
  29. [██▀ • ▄██]
  30. [█▀▀▀ • ▄▄▄█]
  31. [█▀▀▀▀ • ▄▄▄▄█]
  32. [ ▀▀▀▀▀•▄▄▄▄▄ ]
  33. [ ▀▀▀▀█▄▄▄▄ ]
  34. [ ▀▀███▄▄ ]
  35. */
  36. /*
  37. 0 = Blank
  38. 1 = Center Character
  39. 2 = Top
  40. 3 = Bottom
  41. 4 = Full Block
  42. */
  43. func SpinRiteInit(width uint8, length uint8, color []byte) SpinRite {
  44. if len(color) == 0 {
  45. color = ColorText("CYAN ON BLUE")
  46. }
  47. var center uint8 = ((width - 1) / 2)
  48. var start uint8 = ((length - 1) / 2)
  49. var result SpinRite = SpinRite{Width: width,
  50. Length: length,
  51. Color: color,
  52. CenterPos: center,
  53. StartPos: start,
  54. Buffer: make([]uint8, width),
  55. Index: 0,
  56. output: &bytes.Buffer{}}
  57. if Unicode {
  58. result.OutputR = make([]rune, width)
  59. result.Runes = []rune{' ', '\u221e', '\u2580', '\u2584', '\u2588'}
  60. } else {
  61. result.OutputB = make([]byte, width)
  62. result.Bytes = []byte{' ', 0xec, 0xdf, 0xdc, 0xdb}
  63. }
  64. return result
  65. }
  66. func (sr *SpinRite) PosToIndex(pos int8, top bool) (index uint8, level bool) {
  67. if top {
  68. if pos >= int8(sr.Width) {
  69. // wrap around
  70. level = !top
  71. idx := int8(sr.Width) + (int8(sr.Width) - (pos + 1))
  72. index = uint8(idx)
  73. } else {
  74. level = top
  75. index = uint8(pos)
  76. }
  77. } else {
  78. // bottom
  79. if pos < 0 {
  80. level = !top
  81. pos++
  82. index = uint8(-pos)
  83. } else {
  84. level = top
  85. index = uint8(pos)
  86. }
  87. }
  88. return
  89. }
  90. func (sr *SpinRite) Calculate() {
  91. for i := range sr.Buffer {
  92. sr.Buffer[i] = 0
  93. }
  94. sr.Buffer[sr.CenterPos] = 1
  95. // [ S C s ]
  96. var top int8 = int8(sr.CenterPos) - int8(sr.StartPos) + int8(sr.Index)
  97. var bottom int8 = int8(sr.CenterPos) + int8(sr.StartPos) - int8(sr.Index)
  98. var l int8
  99. for l = 0; l < int8(sr.Length); l++ {
  100. var index uint8
  101. var level bool
  102. index, level = sr.PosToIndex(top+l, true)
  103. // log.Println("TOP", l, top+l, index, level)
  104. if level {
  105. sr.Buffer[index] |= 0x02
  106. } else {
  107. sr.Buffer[index] |= 0x04
  108. }
  109. index, level = sr.PosToIndex(bottom-l, false)
  110. // log.Println("BOT", l, bottom-l, index, level)
  111. if level {
  112. sr.Buffer[index] |= 0x02
  113. } else {
  114. sr.Buffer[index] |= 0x04
  115. }
  116. }
  117. // log.Println("Index", sr.Index, "Buffer", sr.Buffer)
  118. var reset bool = true
  119. // Convert from buffer to output
  120. if Unicode {
  121. for l = 0; l < int8(sr.Width); l++ {
  122. switch sr.Buffer[l] {
  123. case 0:
  124. sr.OutputR[l] = sr.Runes[0]
  125. case 1:
  126. sr.OutputR[l] = sr.Runes[1]
  127. reset = false
  128. case 2, 3:
  129. sr.OutputR[l] = sr.Runes[2]
  130. reset = false
  131. case 4, 5:
  132. sr.OutputR[l] = sr.Runes[3]
  133. reset = false
  134. case 6, 7:
  135. sr.OutputR[l] = sr.Runes[4]
  136. }
  137. }
  138. } else {
  139. for l = 0; l < int8(sr.Width); l++ {
  140. switch sr.Buffer[l] {
  141. case 0:
  142. sr.OutputB[l] = sr.Bytes[0]
  143. case 1:
  144. sr.OutputB[l] = sr.Bytes[1]
  145. reset = false
  146. case 2, 3:
  147. sr.OutputB[l] = sr.Bytes[2]
  148. reset = false
  149. case 4, 5:
  150. sr.OutputB[l] = sr.Bytes[3]
  151. reset = false
  152. case 6, 7:
  153. sr.OutputB[l] = sr.Bytes[4]
  154. }
  155. }
  156. }
  157. if reset {
  158. // log.Println("RESET")
  159. sr.Index = 0
  160. }
  161. }
  162. func (sr *SpinRite) Output() []byte {
  163. // var result string
  164. sr.output.Reset()
  165. // sr.Result.Reset()
  166. sr.output.Write(sr.Color)
  167. sr.Calculate()
  168. if Unicode {
  169. // result = string(sr.OutputR)
  170. for _, r := range sr.OutputR {
  171. sr.output.WriteRune(r)
  172. }
  173. // Use the above, benchmark.
  174. // sr.output.WriteString(string(sr.OutputR))
  175. } else {
  176. //result = string(sr.OutputB)
  177. sr.output.Write(sr.OutputB)
  178. }
  179. sr.Index++
  180. // return sr.Color + result
  181. return sr.output.Bytes()
  182. }
  183. type SpinRiteMsg struct {
  184. SpinRite
  185. Messages []string
  186. MsgIndex int
  187. Next bool
  188. }
  189. func SpinRiteMsgInit(width uint8, length uint8, color []byte, messages []string) SpinRiteMsg {
  190. var result SpinRiteMsg = SpinRiteMsg{SpinRite: SpinRiteInit(width, length, color)}
  191. if Unicode {
  192. result.Runes[1] = result.Runes[0]
  193. } else {
  194. result.SpinRite.Bytes[1] = result.SpinRite.Bytes[0]
  195. }
  196. result.Messages = messages
  197. result.MsgIndex = 0
  198. result.Next = false
  199. return result
  200. }
  201. func (sr *SpinRiteMsg) Output() []byte {
  202. sr.output.Reset()
  203. sr.Calculate()
  204. if sr.Next && sr.Index == 0 {
  205. sr.MsgIndex++
  206. if sr.MsgIndex == len(sr.Messages) {
  207. sr.MsgIndex = 0
  208. }
  209. }
  210. if sr.Index != 0 {
  211. sr.Next = true
  212. }
  213. // Place message
  214. var message string = sr.Messages[sr.MsgIndex]
  215. var msg []rune = []rune(message)
  216. var pos int = int(sr.CenterPos)
  217. // Bug: If we're changing to next message (sr.Next == True) ... but the
  218. // message is > SpinRite.Length, it shows the text beyond what it should.
  219. var texthalf int = StringLen([]byte(message)) / 2
  220. // Place text center, outwards. Stopping if there's no space.
  221. for i := 0; i < texthalf+1; i++ {
  222. if Unicode {
  223. if sr.OutputR[pos+i] == ' ' {
  224. if texthalf+i < StringLen([]byte(message)) {
  225. sr.OutputR[pos+i] = []rune(msg)[texthalf+i]
  226. }
  227. } else {
  228. break
  229. }
  230. if i != 0 {
  231. if sr.OutputR[pos-i] == ' ' {
  232. sr.OutputR[pos-i] = []rune(msg)[texthalf-i]
  233. } else {
  234. break
  235. }
  236. }
  237. } else {
  238. if sr.OutputB[pos+i] == ' ' {
  239. if texthalf+i < StringLen([]byte(message)) {
  240. sr.OutputB[pos+i] = byte(msg[texthalf+i])
  241. }
  242. } else {
  243. break
  244. }
  245. if i != 0 {
  246. if sr.OutputB[pos-i] == ' ' {
  247. sr.OutputB[pos-i] = byte(msg[texthalf-i])
  248. } else {
  249. break
  250. }
  251. }
  252. }
  253. }
  254. /*
  255. for i := 0; i < len(msg); i++ {
  256. if Unicode {
  257. if sr.OutputR[pos+i] == ' ' {
  258. sr.OutputR[pos+i] = []rune(msg)[i]
  259. }
  260. } else {
  261. if sr.OutputB[pos+i] == ' ' {
  262. sr.OutputB[pos+i] = byte(msg[i])
  263. }
  264. }
  265. }
  266. */
  267. sr.output.Write(sr.Color)
  268. if Unicode {
  269. for _, r := range sr.OutputR {
  270. sr.output.WriteRune(r)
  271. }
  272. // result = string(sr.OutputR)
  273. } else {
  274. sr.output.Write(sr.OutputB)
  275. // result = string(sr.OutputB)
  276. }
  277. sr.Index++
  278. // return sr.Color + result
  279. return sr.output.Bytes()
  280. }