starfield.cpp 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. #include "starfield.h"
  2. #include "utils.h"
  3. Starfield::Starfield(door::Door &Door, std::mt19937 &Rng)
  4. : door{Door}, rng{Rng}, uni_x{1, Door.width}, uni_y{1, Door.height},
  5. white{door::COLOR::WHITE}, dark{door::COLOR::BLACK, door::ATTR::BRIGHT} {
  6. // std::uniform_int_distribution<int> uni_x(1, mx);
  7. // std::uniform_int_distribution<int> uni_y(1, my);
  8. regenerate();
  9. }
  10. star_pos Starfield::make_pos(void) {
  11. star_pos pos;
  12. do {
  13. pos.x = uni_x(rng);
  14. pos.y = uni_y(rng);
  15. } while (sky.find(pos) != sky.end());
  16. return pos;
  17. }
  18. void Starfield::regenerate(void) {
  19. int mx = door.width;
  20. int my = door.height;
  21. // Make uniform random distribution between 1 and MAX screen size X/Y
  22. sky.clear();
  23. // 10 is too many, 100 is too few. 40 looks ok.
  24. int MAX_STARS = ((mx * my) / 40);
  25. // door.log() << "Generating starmap using " << mx << "," << my << " : "
  26. // << MAX_STARS << " stars." << std::endl;
  27. for (int i = 0; i < MAX_STARS; i++) {
  28. star_pos pos = make_pos();
  29. // store symbol and color in star_pos.
  30. // If we do any animation, we won't be able to rely on the star_pos keeping
  31. // the same position in the set.
  32. pos.symbol = i % 2;
  33. pos.color = i % 5 < 2;
  34. sky.insert(pos);
  35. }
  36. }
  37. void Starfield::display(void) {
  38. door << door::reset << door::cls;
  39. stars[0] = ".";
  40. if (door::unicode) {
  41. stars[1] = "\u2219"; // "\u00b7";
  42. } else {
  43. stars[1] = "\xf9"; // "\xfa";
  44. };
  45. int i = 0;
  46. // lose int i, and set last_pos to -1, -1.
  47. star_pos last_pos;
  48. for (auto &pos : sky) {
  49. bool use_goto = true;
  50. if (i != 0) {
  51. // check last_pos to current position
  52. if (pos.y == last_pos.y) {
  53. // Ok, same row -- try some optimizations
  54. int dx = pos.x - last_pos.x;
  55. if (dx == 0) {
  56. use_goto = false;
  57. } else {
  58. if (dx < 5) {
  59. door << std::string(dx, ' ');
  60. use_goto = false;
  61. } else {
  62. // Use ANSI Cursor Forward
  63. door << "\x1b[" << dx << "C";
  64. use_goto = false;
  65. }
  66. }
  67. }
  68. }
  69. if (use_goto) {
  70. door::Goto star_at(pos.x, pos.y);
  71. door << star_at;
  72. }
  73. if (pos.color)
  74. door << dark;
  75. else
  76. door << white;
  77. if (pos.symbol)
  78. door << stars[0];
  79. else
  80. door << stars[1];
  81. ++i;
  82. last_pos = pos;
  83. last_pos.x++; // star output moves us by one.
  84. }
  85. }
  86. AnimatedStarfield::AnimatedStarfield(door::Door &Door, std::mt19937 &Rng)
  87. : door{Door}, rng{Rng}, uni_x{1, Door.width}, uni_y{1, Door.height},
  88. white{door::COLOR::WHITE}, dark{door::COLOR::BLACK, door::ATTR::BRIGHT} {
  89. mx = door.width;
  90. my = door.height;
  91. cx = mx / 2.0;
  92. cy = my / 2.0;
  93. max_d = distance(0, 0);
  94. // std::uniform_int_distribution<int> uni_x(1, mx);
  95. // std::uniform_int_distribution<int> uni_y(1, my);
  96. regenerate();
  97. }
  98. /**
  99. * @brief Make a moving_star
  100. *
  101. * This makes a new x,y point.
  102. *
  103. * If "centered", we create a point nearer to the center of the screen.
  104. *
  105. * Also, if centered, we don't allow y == cy. FIXES BUG: syncterm 80x25.
  106. *
  107. * moving_star.visible is set by the visibleFunction.
  108. * moving_star.xpos, ypos is initialized from x,y.
  109. * moving_star.movex, movey is initialized from the rise/run from center.
  110. * @param centered
  111. * @return moving_star
  112. */
  113. moving_star AnimatedStarfield::make_pos(bool centered) {
  114. moving_star pos;
  115. do {
  116. pos.x = uni_x(rng);
  117. pos.y = uni_y(rng);
  118. pos.visible = true;
  119. if (centered) {
  120. pos.x /= 4;
  121. pos.x += cx - (cx / 4);
  122. pos.y /= 4;
  123. pos.y += cy - (cy / 4);
  124. }
  125. pos.xpos = pos.x;
  126. pos.ypos = pos.y;
  127. pos.movex = pos.xpos - cx;
  128. pos.movey = pos.ypos - cy;
  129. double bigd = max(abs(pos.movex), abs(pos.movey));
  130. pos.movex /= bigd;
  131. pos.movey /= bigd;
  132. if (visible) {
  133. pos.visible = visible(pos.x, pos.y);
  134. }
  135. } while (centered and (pos.y == cy));
  136. /*
  137. if (get_logger)
  138. get_logger() << pos.x << "," << pos.y << " " << pos.movex << "/"
  139. << pos.movey << std::endl;
  140. */
  141. return pos;
  142. }
  143. void AnimatedStarfield::regenerate(void) {
  144. // Make uniform random distribution between 1 and MAX screen size X/Y
  145. sky.clear();
  146. // 10 is too many, 100 is too few. 40 looks ok.
  147. int MAX_STARS = ((mx * my) / 40);
  148. // door.log() << "Generating starmap using " << mx << "," << my << " : "
  149. // << MAX_STARS << " stars." << std::endl;
  150. for (int i = 0; i < MAX_STARS; i++) {
  151. moving_star pos = make_pos();
  152. pos.symbol = i % 2;
  153. pos.color = i % 5 < 2;
  154. sky.push_back(pos);
  155. }
  156. }
  157. void AnimatedStarfield::display(void) {
  158. door << door::reset << door::cls;
  159. stars[0] = ".";
  160. if (door::unicode) {
  161. stars[1] = "\u2219"; // "\u00b7";
  162. } else {
  163. stars[1] = "\xf9"; // "\xfa";
  164. };
  165. for (auto &pos : sky) {
  166. if (pos.visible) {
  167. door::Goto star_at(pos.x, pos.y);
  168. door << star_at;
  169. if (pos.color)
  170. door << dark;
  171. else
  172. door << white;
  173. if (pos.symbol)
  174. door << stars[0];
  175. else
  176. door << stars[1];
  177. }
  178. }
  179. }
  180. void AnimatedStarfield::animate(void) {
  181. for (auto &star : sky) {
  182. // just start with basic movement
  183. // double speed = abs((cx / 2) - distance(star.movex, star.movey));
  184. double speed = max_d / distance(star.movex, star.movey);
  185. star.xpos += star.movex / (speed * (mx / my));
  186. star.ypos += star.movey / speed;
  187. int nx = int(star.xpos + 0.5);
  188. int ny = int(star.ypos + 0.5);
  189. // watch the bottom of the screen! It'll scroll!
  190. // watch new position we're given!
  191. while ((star.xpos < 1) or (star.xpos >= mx - 1) or (star.ypos < 1) or
  192. (star.ypos >= my - 1)) {
  193. // off the screen! Whoops!
  194. moving_star new_pos = make_pos(true);
  195. nx = new_pos.x;
  196. ny = new_pos.y;
  197. star.xpos = new_pos.x;
  198. star.ypos = new_pos.y;
  199. star.movex = new_pos.movex;
  200. star.movey = new_pos.movey;
  201. // new position updated -- next section will erase and display in new
  202. // position
  203. }
  204. // if ((star.x != int(star.xpos)) or (star.y == int(star.ypos))) {
  205. if ((star.x != nx) or (star.y != ny)) {
  206. // star has moved, update time
  207. if (star.visible) {
  208. door << door::Goto(star.x, star.y) << " ";
  209. };
  210. star.x = nx; // int(star.xpos);
  211. star.y = ny; // int(star.ypos);
  212. if (visible) {
  213. star.visible = visible(star.x, star.y);
  214. }
  215. // still visible?
  216. if (star.visible) {
  217. door << door::Goto(star.x, star.y);
  218. if (star.color)
  219. door << dark;
  220. else
  221. door << white;
  222. if (star.symbol)
  223. door << stars[0];
  224. else
  225. door << stars[1];
  226. }
  227. }
  228. }
  229. }
  230. double AnimatedStarfield::distance(double x, double y) {
  231. return sqrt(((cx - x) * (cx - x)) + ((cy - y) * (cy - y)));
  232. }
  233. void AnimatedStarfield::setVisible(checkVisibleFunction cvf) { visible = cvf; }