bl_demo_bubbles.cpp (6751B)
1 #include <stdlib.h> 2 #include <cmath> 3 #include <vector> 4 5 #include "bl_qt_headers.h" 6 #include "bl_qt_canvas.h" 7 8 struct Bubble { 9 BLPoint p; 10 double r; 11 double x; 12 double a; 13 double b; 14 double c; 15 BLRgba32 colors[2]; 16 }; 17 18 static BL_INLINE BLRgba32 random_rgba32(BLRandom& rnd) noexcept { 19 return BLRgba32(rnd.next_uint32() | 0x55000000u); 20 } 21 22 class MainWindow : public QWidget { 23 Q_OBJECT 24 25 public: 26 QTimer _timer; 27 QComboBox _renderer_select; 28 QCheckBox _limit_fps_check; 29 QSlider _count_slider; 30 QSlider _parameter_slider; 31 QBLCanvas _canvas; 32 33 BLRandom _rnd; 34 std::vector<Bubble> _bubbles; 35 36 bool _animate = true; 37 38 MainWindow() { 39 QVBoxLayout* vBox = new QVBoxLayout(); 40 vBox->setContentsMargins(0, 0, 0, 0); 41 vBox->setSpacing(0); 42 43 QGridLayout* grid = new QGridLayout(); 44 grid->setContentsMargins(5, 5, 5, 5); 45 grid->setSpacing(5); 46 47 QBLCanvas::init_renderer_select_box(&_renderer_select); 48 _limit_fps_check.setText(QLatin1String("Limit FPS")); 49 50 _count_slider.setMinimum(1); 51 _count_slider.setMaximum(5000); 52 _count_slider.setValue(100); 53 _count_slider.setOrientation(Qt::Horizontal); 54 55 _parameter_slider.setMinimum(0); 56 _parameter_slider.setMaximum(1000); 57 _parameter_slider.setValue(100); 58 _parameter_slider.setOrientation(Qt::Horizontal); 59 60 connect(&_renderer_select, SIGNAL(activated(int)), SLOT(onRendererChanged(int))); 61 connect(&_limit_fps_check, SIGNAL(stateChanged(int)), SLOT(onLimitFpsChanged(int))); 62 63 grid->addWidget(new QLabel("Renderer:"), 0, 0); 64 grid->addWidget(&_renderer_select, 0, 1); 65 grid->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding), 0, 3); 66 grid->addWidget(&_limit_fps_check, 0, 4, Qt::AlignRight); 67 68 grid->addWidget(new QLabel("Count:"), 1, 0, Qt::AlignRight); 69 grid->addWidget(&_count_slider, 1, 1, 1, 5); 70 71 grid->addWidget(new QLabel("Param:"), 2, 0, Qt::AlignRight); 72 grid->addWidget(&_parameter_slider, 2, 1, 1, 5); 73 74 _canvas.on_render_blend2d = std::bind(&MainWindow::on_render_blend2d, this, std::placeholders::_1); 75 _canvas.on_render_qt = std::bind(&MainWindow::on_render_qt, this, std::placeholders::_1); 76 77 vBox->addItem(grid); 78 vBox->addWidget(&_canvas); 79 setLayout(vBox); 80 81 connect(&_timer, SIGNAL(timeout()), this, SLOT(onTimer())); 82 connect(new QShortcut(QKeySequence(Qt::Key_P), this), SIGNAL(activated()), SLOT(onToggleAnimate())); 83 84 onInit(); 85 } 86 87 void showEvent(QShowEvent* event) override { _timer.start(); } 88 void hideEvent(QHideEvent* event) override { _timer.stop(); } 89 void keyPressEvent(QKeyEvent* event) override {} 90 91 void onInit() { 92 _rnd.reset(0x123456789ABCDEF); 93 _limit_fps_check.setChecked(true); 94 _updateTitle(); 95 } 96 97 Q_SLOT void onToggleAnimate() { _animate = !_animate; } 98 Q_SLOT void onRendererChanged(int index) { _canvas.set_renderer_type(_renderer_select.itemData(index).toInt()); } 99 Q_SLOT void onLimitFpsChanged(int value) { _timer.setInterval(value ? 1000 / 120 : 0); } 100 101 Q_SLOT void onTimer() { 102 size_t count = unsigned(_count_slider.value()); 103 104 double w = _canvas.image_width(); 105 double h = _canvas.image_height(); 106 107 if (_bubbles.size() > count) { 108 _bubbles.resize(count); 109 } 110 111 double param = double(_parameter_slider.value()) * 0.0001; 112 113 auto newBubble = [](double w, double h, double param, BLRandom& rnd) noexcept -> Bubble { 114 Bubble bubble {}; 115 116 double r = rnd.next_double() * 20.0 + 5.0; 117 118 bubble.p = BLPoint(rnd.next_double() * w, h + r); 119 bubble.r = r; 120 bubble.x = bubble.p.x; 121 bubble.a = rnd.next_double() * 3.0 + 1.0; 122 bubble.b = 0.0; 123 bubble.c = rnd.next_double() * (param * 0.4 + 0.001) + param * 0.02; 124 bubble.colors[0] = random_rgba32(rnd); 125 bubble.colors[1] = BLRgba32(0u); 126 127 return bubble; 128 }; 129 130 if (_animate) { 131 constexpr double PI = 3.14159265359; 132 constexpr uint32_t kAddLimit = 10; 133 134 uint32_t added = 0; 135 while (_bubbles.size() < count && added < kAddLimit) { 136 Bubble bubble = newBubble(w, h, param, _rnd); 137 _bubbles.push_back(bubble); 138 added++; 139 } 140 141 for (Bubble& bubble : _bubbles) { 142 bubble.b += bubble.c; 143 144 if (bubble.b > 2.0) { 145 bubble.b -= 2.0; 146 } 147 148 bubble.p.x = bubble.x + sin(bubble.b * PI) * 20.0; 149 bubble.p.y -= bubble.a; 150 151 if (bubble.p.y < -bubble.r) { 152 bubble = newBubble(w, h, param, _rnd); 153 } 154 } 155 } 156 157 _canvas.update_canvas(true); 158 _updateTitle(); 159 } 160 161 void on_render_blend2d(BLContext& ctx) noexcept { 162 ctx.fill_all(BLRgba32(0xFF000000u)); 163 ctx.set_comp_op(BL_COMP_OP_PLUS); 164 165 BLGradient g; 166 g.set_type(BL_GRADIENT_TYPE_RADIAL); 167 168 double h = _canvas.image_height(); 169 170 for (const Bubble& bubble : _bubbles) { 171 double r = bubble.r; 172 double f = -r * 0.5 + r * (bubble.p.y / h); 173 174 BLGradientStop stops[2] = { 175 BLGradientStop(0.0, bubble.colors[0]), 176 BLGradientStop(1.0, bubble.colors[1]) 177 }; 178 179 g.set_values(BLRadialGradientValues(bubble.p.x, bubble.p.y, bubble.p.x, bubble.p.y + f, r)); 180 g.assign_stops(stops, 2); 181 ctx.fill_rect(BLRectI(int(bubble.p.x - r - 1.0), int(bubble.p.y - r - 1.0), int(r * 2.0) + 2, int(r * 2.0) + 2), g); 182 } 183 } 184 185 void on_render_qt(QPainter& ctx) noexcept { 186 ctx.fillRect(0, 0, _canvas.image_width(), _canvas.image_height(), QColor(0, 0, 0)); 187 ctx.setRenderHint(QPainter::Antialiasing, true); 188 ctx.setCompositionMode(QPainter::CompositionMode_Plus); 189 ctx.setPen(Qt::NoPen); 190 191 double h = _canvas.image_height(); 192 193 for (const Bubble& bubble : _bubbles) { 194 double r = bubble.r; 195 double f = -r * 0.5 + r * (bubble.p.y / h); 196 197 QRadialGradient g(bubble.p.x, bubble.p.y, r, bubble.p.x, bubble.p.y + f); 198 g.setInterpolationMode(QGradient::InterpolationMode::ComponentInterpolation); 199 g.setColorAt(0.0f, bl_rgba_to_qcolor(bubble.colors[0])); 200 g.setColorAt(1.0f, bl_rgba_to_qcolor(bubble.colors[1])); 201 ctx.fillRect(QRect(int(bubble.p.x - r - 1.0), int(bubble.p.y - r - 1.0), int(r * 2.0) + 2, int(r * 2.0) + 2), QBrush(g)); 202 } 203 } 204 205 void _updateTitle() { 206 char buf[256]; 207 snprintf(buf, 256, "Bubbles [%dx%d] [Count=%d] [RenderTime=%.2fms FPS=%.1f]", 208 _canvas.image_width(), 209 _canvas.image_height(), 210 int(_bubbles.size()), 211 _canvas.average_render_time(), 212 _canvas.fps()); 213 214 QString title = QString::fromUtf8(buf); 215 if (title != windowTitle()) 216 setWindowTitle(title); 217 } 218 }; 219 220 int main(int argc, char *argv[]) { 221 QApplication app(argc, argv); 222 MainWindow win; 223 224 win.setMinimumSize(QSize(400, 320)); 225 win.resize(QSize(580, 520)); 226 win.show(); 227 228 return app.exec(); 229 } 230 231 #include "bl_demo_bubbles.moc"