bunny.cpp 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. #include "common/sketchbook.hpp"
  2. using namespace common;
  3. struct vertex
  4. {
  5. float2 origin;
  6. float2 normal;
  7. };
  8. struct polygon
  9. {
  10. std::vector<vertex> vertices;
  11. explicit operator range2f()
  12. {
  13. constexpr auto infinity = float2::one(std::numeric_limits<float>::infinity());
  14. range2f ret {infinity, -infinity};
  15. for(auto&& v : vertices)
  16. {
  17. ret.lower().min(v.origin);
  18. ret.upper().max(v.origin);
  19. }
  20. return ret;
  21. }
  22. };
  23. void update_normals(polygon& p)
  24. {
  25. if(p.vertices.begin() == p.vertices.end())
  26. return;
  27. auto current = p.vertices.begin();
  28. auto previous = p.vertices.end()-1;
  29. do
  30. {
  31. auto direction = current->origin - previous->origin;
  32. previous->normal = rotate(direction, protractor<>::tau(1.f/4));
  33. previous = current++;
  34. }
  35. while(current != p.vertices.end());
  36. }
  37. bool convex_contains(polygon polygon, float2 point)
  38. {
  39. return support::all_of(polygon.vertices, [&point](auto vertex)
  40. {
  41. return vertex.normal(point - vertex.origin) > 0;
  42. });
  43. }
  44. void bezier_way(range<vertex*> buffer)
  45. {
  46. auto start = (buffer.begin()+0)->origin;
  47. auto middle = (buffer.begin()+1)->origin;
  48. auto end = (buffer.begin()+2)->origin;
  49. auto step = 1.f / (buffer.end() - buffer.begin());
  50. auto ratio = 0.f;
  51. for(auto&& vertex : buffer)
  52. {
  53. vertex.origin = way
  54. (
  55. way(start, middle, ratio),
  56. way(middle, end, ratio),
  57. ratio
  58. ), float2{};
  59. ratio += step;
  60. }
  61. }
  62. struct circle
  63. {
  64. float2 center;
  65. float radius;
  66. explicit operator range2f()
  67. {
  68. return {center - radius, center + radius};
  69. }
  70. };
  71. bool contain(range<circle*> circles, float2 point)
  72. {
  73. for(auto [center, radius] : circles)
  74. if(quadrance(center - point) < radius*radius)
  75. return true;
  76. return false;
  77. }
  78. polygon make_nose(float2 aspect)
  79. {
  80. const std::array<vertex, 3> nose_control
  81. {{
  82. {aspect * float2(0.5f, 3.5f/4) + float2(-0.20f, -0.20f)},
  83. {aspect * float2(0.5f, 3.5f/4) + float2(+0.20f, -0.20f)},
  84. // {aspect * float2(0.5f, 3.f/4) + float2(+0.00f, +0.20f)},
  85. {aspect * float2(0.5f, 3.5f/4) + float2(+0.00f, +0.00f)},
  86. }};
  87. polygon nose{};
  88. for(size_t i = 0; i < nose_control.size(); ++i)
  89. {
  90. using support::wrap;
  91. auto& curr = nose_control[i].origin;
  92. auto& next = nose_control[wrap(i+1, nose_control.size())].origin;
  93. auto& prev = nose_control[wrap(i-1, nose_control.size())].origin;
  94. auto curve_control_prev = way(curr, prev, 1.f/2);
  95. auto curve_control_curr = curr;
  96. auto curve_control_next = way(curr, next, 1.f/2);
  97. auto length = std::max(
  98. geom::length(curve_control_curr - curve_control_prev),
  99. geom::length(curve_control_curr - curve_control_next)
  100. );
  101. auto curve_vertex_count = std::max(40.f/length,3.f); // welp magic number 40
  102. auto& vertices = nose.vertices;
  103. auto start = vertices.size();
  104. vertices.resize(start + curve_vertex_count);
  105. vertices[start+0] = vertex{curve_control_prev};
  106. vertices[start+1] = vertex{curve_control_curr};
  107. vertices[start+2] = vertex{curve_control_next};
  108. bezier_way({
  109. vertices.data() + start,
  110. vertices.data() + vertices.size()
  111. });
  112. }
  113. update_normals(nose);
  114. return nose;
  115. };
  116. void some_fur(frame& frame, rgb_pixel color, range<circle*> eyes, const polygon& nose)
  117. {
  118. const auto aspect = frame.size/frame.size.x();
  119. const auto fur_length = support::average(frame.size.x(),frame.size.y())*12/400;
  120. auto fur = frame.begin_sketch();
  121. for(int i = 0; i < (5000 * frame.size.x() / 400); ++i)
  122. {
  123. float2 root;
  124. do
  125. root = trand_float2() * aspect;
  126. while(contain(eyes, root) || convex_contains(nose, root));
  127. root *= frame.size.x();
  128. auto angle = trand_float();
  129. auto stem = rotate_scale(trand_float2() * fur_length,
  130. protractor<>::tau(angle < 1 ? angle : 0));
  131. fur.line(root, root + stem);
  132. }
  133. fur.outline(color);
  134. };
  135. const unsigned fur_seed = trand_int();
  136. std::array<circle,2> eyes;
  137. polygon nose;
  138. range2f nose_bounds;
  139. const framebuffer * furbuffer;
  140. using poke_motion = movement<float2, motion::quadratic_curve>;
  141. melody<poke_motion, poke_motion> poke;
  142. void start(Program& program)
  143. {
  144. // program.size = int2{200,400};
  145. // program.size = int2{300,400};
  146. // program.size = int2{200,400}.mix<1,0>();
  147. // program.size = int2{400,400};
  148. program.fullscreen = true;
  149. furbuffer = &program.request_framebuffer(
  150. program.fullscreen ? program.display.size : program.size,
  151. [](auto frame)
  152. {
  153. auto aspect = frame.size/frame.size.x();
  154. auto radius = aspect.x()/10;
  155. eyes = std::array<circle,2>
  156. {{
  157. {aspect/4, radius},
  158. {aspect/float2(4.f/3, 4.f), radius},
  159. }};
  160. nose = make_nose(aspect);
  161. nose_bounds = range2f(nose) * frame.size.x();
  162. some_fur(frame, 0xbbbbbb_rgb, make_range(eyes), nose);
  163. some_fur(frame, 0xcccccc_rgb, make_range(eyes), nose);
  164. some_fur(frame, 0xdddddd_rgb, make_range(eyes), nose);
  165. some_fur(frame, 0x777777_rgb, make_range(eyes), nose);
  166. some_fur(frame, 0x888888_rgb, make_range(eyes), nose);
  167. }
  168. );
  169. program.draw_loop = [](auto frame, auto delta)
  170. {
  171. tiny_rand.seed({fur_seed, 13});
  172. frame.begin_sketch()
  173. .rectangle(rect{frame.size})
  174. .fill(0xaaaaaa_rgb)
  175. ;
  176. for(auto&& eye : eyes)
  177. {
  178. auto blink = eye;
  179. blink.radius /= 2;
  180. blink.center -= blink.radius/2;
  181. frame.begin_sketch()
  182. .ellipse(range2f(eye) * frame.size.x())
  183. .fill(paint::radial_gradient(
  184. range2f(blink) * frame.size.x(),
  185. {0.f, 1.f},
  186. {
  187. rgba_vector(0xffffff_rgb),
  188. rgba_vector(0x000000_rgb)
  189. }
  190. ))
  191. ;
  192. }
  193. float2 poke_value;
  194. poke.move(poke_value, delta);
  195. { auto nose_sketch = frame.begin_sketch();
  196. nose_sketch.move(nose.vertices.front().origin * frame.size.x() + poke_value);
  197. for(size_t i = 1; i < nose.vertices.size(); ++i)
  198. nose_sketch.vertex(nose.vertices[i].origin * frame.size.x() + poke_value);
  199. auto size = nose_bounds.upper() - nose_bounds.lower();
  200. nose_sketch.fill(paint::radial_gradient(
  201. range2f{
  202. nose_bounds.lower() + size*0.2f,
  203. nose_bounds.lower() + size*0.5f
  204. } + poke_value,
  205. {0.f, 1.f},
  206. {
  207. rgba_vector(0xffffff_rgb),
  208. rgba_vector(0x000000_rgb)
  209. }
  210. ));
  211. }
  212. frame.begin_sketch()
  213. .rectangle(rect2f{frame.size})
  214. .fill(furbuffer->paint())
  215. ;
  216. };
  217. program.mouse_down = [&program](auto position, auto)
  218. {
  219. auto nose_half = (nose_bounds.upper() - nose_bounds.lower())/2;
  220. auto offset = (nose_bounds.lower() + nose_half) - position;
  221. if(abs(offset) < nose_half)
  222. {
  223. program.request_wave({[poke_ratio = nose_half.length()/offset.length()](auto ratio)
  224. {
  225. constexpr float fade_in = 2;
  226. constexpr float fade_out = 2;
  227. float fade = std::min(ratio*fade_in, (1-ratio)*fade_out);
  228. return way(0.f,std::sin(ratio * 170 * poke_ratio), std::min(fade, 1.f));
  229. }, 100ms}, 0);
  230. poke = melody(
  231. poke_motion{100ms, float2::zero(), offset/2},
  232. poke_motion{100ms, offset/2, float2::zero()}
  233. );
  234. }
  235. };
  236. }