odin-blend2d

Odin bindings to Blend2D
Log | Files | Refs | README | LICENSE

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"