bl_demo_elliptic_arc.cpp (6761B)
1 #include <blend2d.h> 2 3 #include "bl_qt_headers.h" 4 #include "bl_qt_canvas.h" 5 6 class MainWindow : public QWidget { 7 Q_OBJECT 8 public: 9 // Widgets. 10 QSlider _x_radius_slider; 11 QSlider _y_radius_slider; 12 QSlider _angle_slider; 13 QCheckBox _large_arc_flag; 14 QCheckBox _sweep_arc_flag; 15 QLabel _bottom_text; 16 QBLCanvas _canvas; 17 18 // Canvas data. 19 BLGradient _gradient; 20 BLPoint _pts[2] {}; 21 size_t _num_points = 2; 22 size_t _closest_vertex = SIZE_MAX; 23 size_t _grabbed_vertex = SIZE_MAX; 24 int _grabbed_x = 0; 25 int _grabbed_y = 0; 26 27 MainWindow() { 28 setWindowTitle(QLatin1String("Elliptic Arcs")); 29 30 QVBoxLayout* vBox = new QVBoxLayout(); 31 vBox->setContentsMargins(0, 0, 0, 0); 32 vBox->setSpacing(0); 33 34 QGridLayout* grid = new QGridLayout(); 35 grid->setContentsMargins(5, 5, 5, 5); 36 grid->setSpacing(5); 37 38 _x_radius_slider.setOrientation(Qt::Horizontal); 39 _x_radius_slider.setMinimum(1); 40 _x_radius_slider.setMaximum(500); 41 _x_radius_slider.setSliderPosition(131); 42 43 _y_radius_slider.setOrientation(Qt::Horizontal); 44 _y_radius_slider.setMinimum(1); 45 _y_radius_slider.setMaximum(500); 46 _y_radius_slider.setSliderPosition(143); 47 48 _angle_slider.setOrientation(Qt::Horizontal); 49 _angle_slider.setMinimum(-360); 50 _angle_slider.setMaximum(360); 51 _angle_slider.setSliderPosition(0); 52 53 _large_arc_flag.setText(QLatin1String("Large Arc Flag")); 54 _sweep_arc_flag.setText(QLatin1String("Sweep Arc Flag")); 55 56 _bottom_text.setTextInteractionFlags(Qt::TextSelectableByMouse); 57 _bottom_text.setMargin(5); 58 59 connect(&_x_radius_slider, SIGNAL(valueChanged(int)), SLOT(onParameterChanged(int))); 60 connect(&_y_radius_slider, SIGNAL(valueChanged(int)), SLOT(onParameterChanged(int))); 61 connect(&_angle_slider, SIGNAL(valueChanged(int)), SLOT(onParameterChanged(int))); 62 connect(&_large_arc_flag, SIGNAL(stateChanged(int)), SLOT(onParameterChanged(int))); 63 connect(&_sweep_arc_flag, SIGNAL(stateChanged(int)), SLOT(onParameterChanged(int))); 64 65 _canvas.on_render_blend2d = std::bind(&MainWindow::onRender, this, std::placeholders::_1); 66 _canvas.on_mouse_event = std::bind(&MainWindow::on_mouse_event, this, std::placeholders::_1); 67 68 grid->addWidget(new QLabel("X Radius:"), 0, 0, Qt::AlignRight); 69 grid->addWidget(&_x_radius_slider, 0, 1); 70 grid->addWidget(&_large_arc_flag, 0, 2); 71 72 grid->addWidget(new QLabel("Y Radius:"), 1, 0, Qt::AlignRight); 73 grid->addWidget(&_y_radius_slider, 1, 1); 74 grid->addWidget(&_sweep_arc_flag, 1, 2); 75 76 grid->setSpacing(5); 77 grid->addWidget(new QLabel("Angle:"), 2, 0, Qt::AlignRight); 78 grid->addWidget(&_angle_slider, 2, 1, 1, 2); 79 80 vBox->addItem(grid); 81 vBox->addWidget(&_canvas); 82 vBox->addWidget(&_bottom_text); 83 setLayout(vBox); 84 85 onInit(); 86 } 87 88 void keyPressEvent(QKeyEvent *event) override {} 89 90 void onInit() { 91 _pts[0].reset(124, 180); 92 _pts[1].reset(296, 284); 93 _gradient.add_stop(0.0, BLRgba32(0xFF000000u)); 94 _gradient.add_stop(1.0, BLRgba32(0xFFFFFFFFu)); 95 } 96 97 size_t getClosestVertex(BLPoint p, double maxDistance) noexcept { 98 size_t closestIndex = SIZE_MAX; 99 double closestDistance = std::numeric_limits<double>::max(); 100 for (size_t i = 0; i < _num_points; i++) { 101 double d = std::hypot(_pts[i].x - p.x, _pts[i].y - p.y); 102 if (d < closestDistance && d < maxDistance) { 103 closestIndex = i; 104 closestDistance = d; 105 } 106 } 107 return closestIndex; 108 } 109 110 void on_mouse_event(QMouseEvent* event) { 111 double mx = event->position().x(); 112 double my = event->position().y(); 113 114 if (event->type() == QEvent::MouseButtonPress) { 115 if (event->button() == Qt::LeftButton) { 116 if (_closest_vertex != SIZE_MAX) { 117 _grabbed_vertex = _closest_vertex; 118 _grabbed_x = mx; 119 _grabbed_y = my; 120 _canvas.update_canvas(); 121 } 122 } 123 } 124 125 if (event->type() == QEvent::MouseButtonRelease) { 126 if (event->button() == Qt::LeftButton) { 127 if (_grabbed_vertex != SIZE_MAX) { 128 _grabbed_vertex = SIZE_MAX; 129 _canvas.update_canvas(); 130 } 131 } 132 } 133 134 if (event->type() == QEvent::MouseMove) { 135 if (_grabbed_vertex == SIZE_MAX) { 136 _closest_vertex = getClosestVertex(BLPoint(mx, my), 5); 137 _canvas.update_canvas(); 138 } 139 else { 140 _pts[_grabbed_vertex] = BLPoint(mx, my); 141 _canvas.update_canvas(); 142 } 143 } 144 } 145 146 Q_SLOT void onParameterChanged(int value) { 147 _canvas.update_canvas(); 148 } 149 150 void onRender(BLContext& ctx) { 151 ctx.fill_all(BLRgba32(0xFF000000u)); 152 153 BLPoint radius(_x_radius_slider.value(), _y_radius_slider.value()); 154 BLPoint start(_pts[0]); 155 BLPoint end(_pts[1]); 156 157 double PI = 3.14159265359; 158 double angle = (double(_angle_slider.value()) / 180.0) * PI; 159 160 bool largeArcFlag = _large_arc_flag.isChecked(); 161 bool sweepArcFlag = _sweep_arc_flag.isChecked(); 162 163 // Render all arcs before rendering the one that is selected. 164 BLPath p; 165 p.move_to(start); 166 p.elliptic_arc_to(radius, angle, false, false, end); 167 p.move_to(start); 168 p.elliptic_arc_to(radius, angle, false, true, end); 169 p.move_to(start); 170 p.elliptic_arc_to(radius, angle, true, false, end); 171 p.move_to(start); 172 p.elliptic_arc_to(radius, angle, true, true, end); 173 ctx.stroke_path(p, BLRgba32(0x40FFFFFFu)); 174 175 // Render elliptic arc based on the given parameters. 176 p.clear(); 177 p.move_to(start); 178 p.elliptic_arc_to(radius, angle, largeArcFlag, sweepArcFlag, end); 179 ctx.stroke_path(p, BLRgba32(0xFFFFFFFFu)); 180 181 // Render all points of the path (as the arc was split into segments). 182 renderPathPoints(ctx, p, BLRgba32(0xFF808080)); 183 184 // Render the rest of the UI (draggable points). 185 for (size_t i = 0; i < _num_points; i++) { 186 ctx.fill_circle(_pts[i].x, _pts[i].y, 2.5, i == _closest_vertex ? BLRgba32(0xFF00FFFFu) : BLRgba32(0xFF007FFFu)); 187 } 188 189 char buf[256]; 190 snprintf(buf, 256, "<path d=\"M%g %g A%g %g %g %d %d %g %g\" />", start.x, start.y, radius.x, radius.y, angle / PI * 180, largeArcFlag, sweepArcFlag, end.x, end.y); 191 _bottom_text.setText(QString::fromUtf8(buf)); 192 } 193 194 void renderPathPoints(BLContext& ctx, const BLPath& path, BLRgba32 color) noexcept { 195 size_t count = path.size(); 196 const BLPoint* vtx = path.vertex_data(); 197 198 ctx.set_fill_style(color); 199 for (size_t i = 0; i < count; i++) { 200 if (!std::isfinite(vtx[i].x)) 201 continue; 202 ctx.fill_circle(vtx[i].x, vtx[i].y, 2.0); 203 } 204 } 205 }; 206 207 int main(int argc, char *argv[]) { 208 QApplication app(argc, argv); 209 MainWindow win; 210 211 win.setMinimumSize(QSize(400, 320)); 212 win.resize(QSize(580, 520)); 213 win.show(); 214 215 return app.exec(); 216 } 217 218 #include "bl_demo_elliptic_arc.moc"