3.7. The SGL Procedural Interface

The SGL's object-oriented API is relatively simple and straightforward for developers familiar with the concepts of inheritance, polymorphism, virtual functions, and method overriding, but these concepts can be a significant barrier to novice programmers. To address this, the SGL provides a simplified procedural interface that opens up graphics programming in C++ to those less familiar with OO development concepts.

The procedural API limits an application to just one graphical window. The developer calls functions to register callbacks that respond to major events. Example 3.4 uses the procedural API within an application that allows a user to place or move a box within its window by selecting a position with the mouse.

Example 3.4. Using the Procedural Interface

#include <GL/sgl.hpp>

// Box initially is out of the window
double box_x = -10;
double box_y = -10;

void draw() {
    sgl::set_color(sgl::BLUE);
    sgl::fill_rectangle(box_x - 10, box_y - 10, 20, 20);
}

void mouse_pressed(double x, double y, sgl::MouseButton) {
    box_x = x;
    box_y = y;
    sgl::update_window();
}

int main() {
    sgl::create_window("BoxWindow", 0, 300, 0, 300);
    sgl::set_paint_function(draw);
    sgl::set_mouse_pressed_function(mouse_pressed);
    sgl::run_window();
}


Note that Example 3.4 uses no explicit objects. The sgl::create_window function behind the scenes allocates a single global graphical window object. The sgl::set_paint_function and sgl::set_mouse_pressed_function functions allow the programmer to register the appropriate event handler callback functions. The procedural API provides simmilar functions to track mouse movement and dragging and key presses. The run_window function calls the run method of the hidden global graphical window object.

Example 3.4 used global variables to maintain the state of the system (that is, the location of the box within the window). The OO approach would have avoided the use of global variables, making them instance variables within a graphical window object. Even though the global variables may be appropriate for the simple Example 3.4, good programming practice discourages the use of global variables. Fortunately, it is possible to use the procedural interface along with simple OO techniques that avoid inheritance and polymorphism. Example 3.5 uses C++ lambda functions to serve as callbacks to methods within an object. Note that the object b is local to the main function, and the application declares no global variables. It is safe for the lambda functions to capture local variable b by reference because the sgl::run_window function does not return until the graphical program is finished.

Example 3.5. The Procedural Interface without Global Variables

#include <GL/sgl.hpp>

class GraphicalBox {
    int x;
    int y;
public:
    GraphicalBox(int x, int y): x(x), y(y) {}

    void draw() {
        sgl::set_color(sgl::BLUE);
        sgl::fill_rectangle(x - 10, y - 10, 20, 20);
    }

    void mouse_pressed(double mx, double my, sgl::MouseButton) {
        x = static_cast<int>(mx);
        y = static_cast<int>(my);
        sgl::update_window();
    }
};


int main() {
    GraphicalBox b{-10, -10};  // Initially out of the window
    sgl::create_window("BoxWindow", 0, 300, 0, 300);
    sgl::set_paint_function([&b]() { b.draw(); });
    sgl::set_mouse_pressed_function([&b](double x, double y, sgl::MouseButton mb) {
                                        b.mouse_pressed(x, y, mb);
                                    });
    sgl::run_window();
}



Copyright  ©2019 Richard L. HaltermanVersion 0.9.5February 17, 2019
Creative Commons License This work is licensed under a Creative Commons License.