range_intersection.cpp 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. /* You can drag the two ranges(axis-aligned rectangles) around, and resize
  2. * them by holding the control key and dragging the bounds(corners).
  3. * The result of the intersection function is highlighted green if it's valid
  4. * and red otherwise. There is also an arrow drawn pointing from the lower
  5. * bound to the upper bound of the intersection. It seems to carry some
  6. * information in the invalid case that might be useful?
  7. */
  8. #include "common/sketchbook.hpp"
  9. constexpr float corner_radius = 14.f;
  10. constexpr float initial_range_size = .25f;
  11. constexpr float2 half = float2::one(.5f);
  12. range2f a {};
  13. range2f b {};
  14. bool resize_mode = false;
  15. range2f* dragged = nullptr;
  16. float2* dragged_corner = nullptr;
  17. bool is_near(float2 corner, float2 position);
  18. sketch arrow(sketch s, float2 start, float2 end);
  19. void print_test_case();
  20. void start(Program& program)
  21. {
  22. program.key_down = [](scancode code, keycode)
  23. {
  24. switch(code)
  25. {
  26. case scancode::lctrl:
  27. case scancode::rctrl:
  28. resize_mode = true;
  29. break;
  30. case scancode::space:
  31. print_test_case();
  32. break;
  33. default: break;
  34. }
  35. };
  36. program.key_up = [&program](scancode code, keycode)
  37. {
  38. switch(code)
  39. {
  40. case scancode::leftbracket:
  41. case scancode::c:
  42. if(pressed(scancode::rctrl) || pressed(scancode::lctrl))
  43. case scancode::escape:
  44. program.end();
  45. break;
  46. case scancode::lctrl:
  47. case scancode::rctrl:
  48. resize_mode = false;
  49. break;
  50. default: break;
  51. }
  52. };
  53. program.mouse_down = [](float2 position, auto)
  54. {
  55. if(resize_mode)
  56. {
  57. if(is_near(b.upper(), position))
  58. dragged_corner = &b.upper();
  59. else if(is_near(b.lower(), position))
  60. dragged_corner = &b.lower();
  61. else if(is_near(a.upper(), position))
  62. dragged_corner = &a.upper();
  63. else if(is_near(a.lower(), position))
  64. dragged_corner = &a.lower();
  65. }
  66. else
  67. {
  68. if(b.contains(position))
  69. dragged = &b;
  70. else if (a.contains(position))
  71. dragged = &a;
  72. }
  73. };
  74. program.mouse_up = [](auto, auto)
  75. {
  76. dragged = nullptr;
  77. dragged_corner = nullptr;
  78. };
  79. program.mouse_move = [](auto, float2 motion)
  80. {
  81. if(dragged)
  82. (*dragged) += motion;
  83. if(dragged_corner)
  84. (*dragged_corner) += motion;
  85. };
  86. program.draw_once = [](auto frame)
  87. {
  88. const auto size = frame.size * initial_range_size;
  89. auto r = rect{size, frame.size/2, float2::one(0.25f)};
  90. a = r;
  91. b = rect{r, float2::one(0.75f)};
  92. };
  93. program.draw_loop = [](auto frame, auto)
  94. {
  95. frame.begin_sketch()
  96. .rectangle(rect{ frame.size })
  97. .fill(0xffffff_rgb)
  98. ;
  99. frame.begin_sketch()
  100. .rectangle(a)
  101. .fill(0xaaaaaa_rgb)
  102. ;
  103. frame.begin_sketch()
  104. .rectangle(b)
  105. .fill(0xaaaaaa_rgb)
  106. ;
  107. const auto c = intersection(a,b);
  108. frame.begin_sketch()
  109. .rectangle(c.fix())
  110. .fill(c.valid() ? 0x00ff00_rgb : 0xff0000_rgb)
  111. ;
  112. arrow(frame.begin_sketch(), c.lower(), c.upper())
  113. .line_width(1).outline(0x770077_rgb)
  114. ;
  115. if(resize_mode)
  116. {
  117. frame.begin_sketch()
  118. .ellipse(rect{float2::one(corner_radius), b.upper(), half})
  119. .ellipse(rect{float2::one(corner_radius), b.lower(), half})
  120. .ellipse(rect{float2::one(corner_radius), a.upper(), half})
  121. .ellipse(rect{float2::one(corner_radius), a.lower(), half})
  122. .line_width(1).outline(0x555555_rgb)
  123. ;
  124. }
  125. };
  126. }
  127. bool is_near(float2 corner, float2 position)
  128. {
  129. return (corner - position).magnitude() < corner_radius * corner_radius;
  130. }
  131. sketch arrow(sketch s, float2 start, float2 end)
  132. {
  133. float2 direction = end - start;
  134. float2 perpendicular = direction.mix<1,0>() * float2(-1.f,1.f);
  135. float2 shaft = start + direction * .8f;
  136. s.line(start, end);
  137. s.line(end, shaft + perpendicular * .1f);
  138. s.line(end, shaft - perpendicular * .1f);
  139. return s;
  140. }
  141. void print_test_case()
  142. {
  143. const auto i = intersection(a,b);
  144. std::cout << '{' << '\n';
  145. std::cout << '\t' << "auto a = range{" << "vector" << a.lower() << ", vector" << a.upper() << "};" << '\n';
  146. std::cout << '\t' << "auto b = range{" << "vector" << b.lower() << ", vector" << b.upper() << "};" << '\n';
  147. std::cout << '\t' << "auto i = range{" << "vector" << i.lower() << ", vector" << i.upper() << "};" << '\n';
  148. std::cout << '\t' << "assert(intersection(a,b) == i);" << '\n';
  149. std::cout << '\t' << "assert(i.valid() == " << (i.valid() ? "true" : "false") << ");" << '\n';
  150. std::cout << '\t' << "assert(i.valid() == intersects(a,b));" << '\n';
  151. std::cout << '}' << '\n';
  152. std::cout << '\n';
  153. }