blob.cpp 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. // This file is part of Desktop App Toolkit,
  2. // a set of libraries for developing nice desktop applications.
  3. //
  4. // For license and copyright information please follow this link:
  5. // https://github.com/desktop-app/legal/blob/master/LEGAL
  6. //
  7. #include "ui/paint/blob.h"
  8. #include "base/random.h"
  9. #include "ui/painter.h"
  10. #include <QtGui/QPainterPath>
  11. #include <QtCore/QtMath>
  12. namespace Ui::Paint {
  13. namespace {
  14. constexpr auto kMaxSpeed = 8.2;
  15. constexpr auto kMinSpeed = 0.8;
  16. constexpr auto kMinSegmentSpeed = 0.017;
  17. constexpr auto kSegmentSpeedDiff = 0.003;
  18. [[nodiscard]] float64 RandomAdditional() {
  19. return (base::RandomValue<int>() % 100 / 100.);
  20. }
  21. } // namespace
  22. Blob::Blob(int n, float minSpeed, float maxSpeed)
  23. : _segmentsCount(n)
  24. , _minSpeed(minSpeed ? minSpeed : kMinSpeed)
  25. , _maxSpeed(maxSpeed ? maxSpeed : kMaxSpeed)
  26. , _pen(Qt::NoBrush, 0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin) {
  27. }
  28. void Blob::generateBlob() {
  29. for (auto i = 0; i < _segmentsCount; i++) {
  30. generateSingleValues(i);
  31. // Fill nexts.
  32. generateTwoValues(i);
  33. // Fill currents.
  34. generateTwoValues(i);
  35. }
  36. }
  37. void Blob::generateSingleValues(int i) {
  38. auto &segment = segmentAt(i);
  39. segment.progress = 0.;
  40. segment.speed = kMinSegmentSpeed
  41. + kSegmentSpeedDiff * std::abs(RandomAdditional());
  42. }
  43. void Blob::update(float level, float speedScale, float64 rate) {
  44. for (auto i = 0; i < _segmentsCount; i++) {
  45. auto &segment = segmentAt(i);
  46. segment.progress += (_minSpeed + level * _maxSpeed * speedScale)
  47. * segment.speed
  48. * rate;
  49. if (segment.progress >= 1) {
  50. generateSingleValues(i);
  51. generateTwoValues(i);
  52. }
  53. }
  54. }
  55. void Blob::setRadiuses(Radiuses values) {
  56. _radiuses = values;
  57. }
  58. Blob::Radiuses Blob::radiuses() const {
  59. return _radiuses;
  60. }
  61. RadialBlob::RadialBlob(int n, float minScale, float minSpeed, float maxSpeed)
  62. : Blob(n, minSpeed, maxSpeed)
  63. , _segmentLength((4.0 / 3.0) * std::tan(M_PI / (2 * n)))
  64. , _minScale(minScale)
  65. , _segmentAngle(360. / n)
  66. , _angleDiff(_segmentAngle * 0.05)
  67. , _segments(n) {
  68. }
  69. void RadialBlob::paint(QPainter &p, const QBrush &brush, float outerScale) {
  70. auto path = QPainterPath();
  71. auto m = QTransform();
  72. const auto scale = (_minScale + (1. - _minScale) * _scale) * outerScale;
  73. if (scale == 0.) {
  74. return;
  75. }
  76. p.save();
  77. if (scale != 1.) {
  78. p.scale(scale, scale);
  79. }
  80. for (auto i = 0; i < _segmentsCount; i++) {
  81. const auto &segment = _segments[i];
  82. const auto nextIndex = i + 1 < _segmentsCount ? (i + 1) : 0;
  83. const auto nextSegment = _segments[nextIndex];
  84. const auto progress = segment.progress;
  85. const auto progressNext = nextSegment.progress;
  86. const auto r1 = segment.radius.current * (1. - progress)
  87. + segment.radius.next * progress;
  88. const auto r2 = nextSegment.radius.current * (1. - progressNext)
  89. + nextSegment.radius.next * progressNext;
  90. const auto angle1 = segment.angle.current * (1. - progress)
  91. + segment.angle.next * progress;
  92. const auto angle2 = nextSegment.angle.current * (1. - progressNext)
  93. + nextSegment.angle.next * progressNext;
  94. const auto l = _segmentLength * (std::min(r1, r2)
  95. + (std::max(r1, r2) - std::min(r1, r2)) / 2.);
  96. m.reset();
  97. m.rotate(angle1);
  98. const auto pointStart1 = m.map(QPointF(0, -r1));
  99. const auto pointStart2 = m.map(QPointF(l, -r1));
  100. m.reset();
  101. m.rotate(angle2);
  102. const auto pointEnd1 = m.map(QPointF(0, -r2));
  103. const auto pointEnd2 = m.map(QPointF(-l, -r2));
  104. if (i == 0) {
  105. path.moveTo(pointStart1);
  106. }
  107. path.cubicTo(pointStart2, pointEnd2, pointEnd1);
  108. }
  109. p.setBrush(Qt::NoBrush);
  110. p.setPen(_pen);
  111. p.fillPath(path, brush);
  112. p.drawPath(path);
  113. p.restore();
  114. }
  115. void RadialBlob::generateTwoValues(int i) {
  116. auto &radius = _segments[i].radius;
  117. auto &angle = _segments[i].angle;
  118. const auto radDiff = _radiuses.max - _radiuses.min;
  119. angle.setNext(_segmentAngle * i + RandomAdditional() * _angleDiff);
  120. radius.setNext(_radiuses.min + std::abs(RandomAdditional()) * radDiff);
  121. }
  122. void RadialBlob::update(float level, float speedScale, float64 rate) {
  123. _scale = level;
  124. Blob::update(level, speedScale, rate);
  125. }
  126. Blob::Segment &RadialBlob::segmentAt(int i) {
  127. return _segments[i];
  128. };
  129. LinearBlob::LinearBlob(
  130. int n,
  131. Direction direction,
  132. float minSpeed,
  133. float maxSpeed)
  134. : Blob(n + 1)
  135. , _topDown(direction == Direction::TopDown ? 1 : -1)
  136. , _segments(_segmentsCount) {
  137. }
  138. void LinearBlob::paint(QPainter &p, const QBrush &brush, int width) {
  139. if (!width) {
  140. return;
  141. }
  142. auto path = QPainterPath();
  143. const auto left = 0;
  144. const auto right = width;
  145. path.moveTo(right, 0);
  146. path.lineTo(left, 0);
  147. const auto n = float(_segmentsCount - 1);
  148. p.save();
  149. for (auto i = 0; i < _segmentsCount; i++) {
  150. const auto &segment = _segments[i];
  151. if (!i) {
  152. const auto &progress = segment.progress;
  153. const auto r1 = segment.radius.current * (1. - progress)
  154. + segment.radius.next * progress;
  155. const auto y = r1 * _topDown;
  156. path.lineTo(left, y);
  157. } else {
  158. const auto &prevSegment = _segments[i - 1];
  159. const auto &progress = prevSegment.progress;
  160. const auto r1 = prevSegment.radius.current * (1. - progress)
  161. + prevSegment.radius.next * progress;
  162. const auto &progressNext = segment.progress;
  163. const auto r2 = segment.radius.current * (1. - progressNext)
  164. + segment.radius.next * progressNext;
  165. const auto x1 = (right - left) / n * (i - 1);
  166. const auto x2 = (right - left) / n * i;
  167. const auto cx = x1 + (x2 - x1) / 2;
  168. const auto y1 = r1 * _topDown;
  169. const auto y2 = r2 * _topDown;
  170. path.cubicTo(
  171. QPointF(cx, y1),
  172. QPointF(cx, y2),
  173. QPointF(x2, y2)
  174. );
  175. }
  176. }
  177. path.lineTo(right, 0);
  178. p.setBrush(Qt::NoBrush);
  179. p.setPen(_pen);
  180. p.fillPath(path, brush);
  181. p.drawPath(path);
  182. p.restore();
  183. }
  184. void LinearBlob::generateTwoValues(int i) {
  185. auto &radius = _segments[i].radius;
  186. const auto radDiff = _radiuses.max - _radiuses.min;
  187. radius.setNext(_radiuses.min + std::abs(RandomAdditional()) * radDiff);
  188. }
  189. Blob::Segment &LinearBlob::segmentAt(int i) {
  190. return _segments[i];
  191. };
  192. } // namespace Ui::Paint