<commit_before>//
// Created by valdemar on 14.10.17.
//

#include "UIController.h"

#include <imgui_impl/imgui_widgets.h>
#include <imgui_impl/imgui_impl_glfw_gl3.h>
#include <imgui_impl/style.h>
#include <fontawesome.h>

#include <glm/gtc/type_ptr.hpp>

struct UIController::wnd_t {
    bool show_style_editor = false;
    bool show_fps_overlay = true;
    bool show_info = true;
    bool show_playback_control = true;
    bool show_ui_help = false;
    bool show_shortcuts_help = false;
    bool show_metrics = false;
    bool show_mouse_pos_tooltip = false;
};

UIController::UIController(Camera *camera, Config *conf)
    : camera_(camera)
    , conf_(conf) {

    // Setup ImGui binding
    ImGui_ImplGlfwGL3_Init(glfwGetCurrentContext(), true);
    wnd_ = std::make_unique<wnd_t>();

    setup_custom_style(false);

    auto &io = ImGui::GetIO();

    io.IniFilename = "rewindviewer.ini";

    //Load and merge fontawesome to current font
    io.Fonts->AddFontDefault();
    const ImWchar icons_range[] = {ICON_MIN_FA, ICON_MAX_FA, 0};
    ImFontConfig icons_config;
    icons_config.MergeMode = true;
    icons_config.PixelSnapH = true;
    io.Fonts->AddFontFromFileTTF("resources/fonts/fontawesome-webfont.ttf", 14.0f, &icons_config, icons_range);
    //Need to call it here, otherwise fontawesome glyph ranges would be corrupted on Windows
    ImGui_ImplGlfwGL3_CreateDeviceObjects();
}

UIController::~UIController() {
    // Cleanup imgui
    ImGui_ImplGlfwGL3_Shutdown();
}

void UIController::next_frame(Scene *scene, NetListener::ConStatus client_status) {
    // Start new frame
    ImGui_ImplGlfwGL3_NewFrame();

    //Clear data option
    if (ImGui::BeginMainMenuBar()) {
        if (ImGui::BeginMenu(ICON_FA_PENCIL_SQUARE_O " Edit", true)) {
            //if (ImGui::MenuItem(ICON_FA_RECYCLE " Clear frames data", "CTRL+R", false, scene->has_data())) {
            //    scene->clear_data(true);
            //}
            ImGui::Checkbox("Close window by Escape key", &conf_->ui.close_with_esc);
            ImGui::EndMenu();
        }
        ImGui::EndMainMenuBar();
    }

    //Update windows status
    main_menu_bar();

    if (wnd_->show_fps_overlay) {
        fps_overlay_widget(client_status);
    }
    if (wnd_->show_info) {
        info_widget(scene);
    }
    if (wnd_->show_playback_control) {
        playback_control_widget(scene);
    }
    if (wnd_->show_style_editor) {
        ImGui::Begin("Style editor", &wnd_->show_style_editor);
        ImGui::ShowStyleEditor();
        ImGui::End();
    }
    if (wnd_->show_metrics) {
        ImGui::ShowMetricsWindow(&wnd_->show_metrics);
    }
    if (wnd_->show_ui_help) {
        ImGui::Begin(ICON_FA_INFO_CIRCLE " UI guide", &wnd_->show_ui_help, ImGuiWindowFlags_AlwaysAutoResize);
        ImGui::ShowUserGuide();
        ImGui::End();
    }
    if (wnd_->show_shortcuts_help) {
        ImGui::Begin(ICON_FA_KEYBOARD_O " Controls help",
                     &wnd_->show_shortcuts_help,
                     ImGuiWindowFlags_AlwaysAutoResize);
        ImGui::BulletText("Mouse drag on map to move camera");
        ImGui::BulletText("Mouse wheel to zoom");
        ImGui::BulletText("Space - play/stop frame playback");
        ImGui::BulletText("Left, right arrow - manually change frames");
        ImGui::BulletText("Esc - close application");
        ImGui::BulletText("Ctrl+g - go to tick");
        ImGui::BulletText("g - Toggle grid draw state");
        ImGui::BulletText("p - Show tooltip with cursor world coordinates");
        ImGui::BulletText("1-5 - Toggle layers visibility");

        ImGui::End();
    }

    const auto &io = ImGui::GetIO();
    if (!io.WantCaptureMouse && wnd_->show_mouse_pos_tooltip) {
        ImGui::BeginTooltip();
        auto wmouse_pos = camera_->screen2world(io.MousePos);
        ImGui::Text("(%.3f, %.3f)", wmouse_pos.x, wmouse_pos.y);
        ImGui::EndTooltip();
    }

    //Checking hotkeys
    if (!io.WantTextInput) {
        if (key_pressed_once(GLFW_KEY_SPACE)) {
            autoplay_scene_ = !autoplay_scene_;
        }
        if (key_pressed_once(GLFW_KEY_G) && !io.KeyCtrl) {
            conf_->scene.show_grid = !conf_->scene.show_grid;
        }
        if (key_pressed_once(GLFW_KEY_P)) {
            wnd_->show_mouse_pos_tooltip = !wnd_->show_mouse_pos_tooltip;
        }
        if (io.KeysDown[GLFW_KEY_D] && io.KeyCtrl) {
            developer_mode_ = true;
        }
        auto &enabled_layers = conf_->scene.enabled_layers;
        for (size_t layer_idx = 0; layer_idx < enabled_layers.size(); ++layer_idx) {
            if (key_pressed_once(static_cast<int>(GLFW_KEY_1 + layer_idx))) {
                enabled_layers[layer_idx] = !enabled_layers[layer_idx];
            }
        }
        if (scene->has_data() && io.KeysDown[GLFW_KEY_R] && io.KeyCtrl) {
            scene->clear_data(false);
        }
    }

    request_exit_ = conf_->ui.close_with_esc && io.KeysDown[GLFW_KEY_ESCAPE];

    //Hittest for detailed unit info
    if (!ImGui::GetIO().WantCaptureMouse) {
        scene->show_detailed_info(camera_->screen2world(ImGui::GetIO().MousePos));
    }

    //Background color
    glClearColor(conf_->ui.clear_color.r, conf_->ui.clear_color.g, conf_->ui.clear_color.b, 1.0f);
}

void UIController::frame_end() {
    ImGui::Render();
}

bool UIController::close_requested() {
    return request_exit_;
}

void UIController::main_menu_bar() {
    if (ImGui::BeginMainMenuBar()) {
        if (ImGui::BeginMenu(ICON_FA_EYE " View", true)) {
            ImGui::Checkbox("FPS overlay", &wnd_->show_fps_overlay);
            ImGui::Checkbox("Utility window", &wnd_->show_info);
            if (developer_mode_) {
                ImGui::Separator();
                ImGui::Checkbox("Style editor", &wnd_->show_style_editor);
                ImGui::Checkbox("Metrics", &wnd_->show_metrics);
            }
            ImGui::EndMenu();
        }
        if (ImGui::BeginMenu(ICON_FA_QUESTION_CIRCLE_O " Help", true)) {
            ImGui::MenuItem(ICON_FA_INFO_CIRCLE " UI guide", nullptr, &wnd_->show_ui_help);
            ImGui::MenuItem(ICON_FA_KEYBOARD_O " Controls", nullptr, &wnd_->show_shortcuts_help);
            ImGui::EndMenu();
        }
        ImGui::EndMainMenuBar();
    }
}

void UIController::fps_overlay_widget(NetListener::ConStatus net_status) {
    ImGui::SetNextWindowPos(ImVec2(10, 20));
    const auto flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize
                       | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings;
    if (ImGui::Begin("FPS Overlay", &wnd_->show_fps_overlay, ImVec2{0, 0}, 0.3, flags)) {
        ImGui::BeginGroup();
        ImGui::TextColored({1.0, 1.0, 0.0, 1.0}, "FPS %.1f", ImGui::GetIO().Framerate);
        ImGui::SameLine();
        ImGui::Text("[%.1f ms]", 1000.0f / ImGui::GetIO().Framerate);
        ImGui::EndGroup();
        std::string strstatus;
        ImVec4 color;
        static const float intensity = 1.0;
        switch (net_status) {
            case NetListener::ConStatus::WAIT:
                strstatus = "WAITING";
                color = {intensity, intensity, 0.0, 1.0};
                break;
            case NetListener::ConStatus::ESTABLISHED:
                strstatus = "CONNECTED";
                color = {0.0, intensity, 0.0, 1.0};
                break;
            case NetListener::ConStatus::CLOSED:
                strstatus = "CLOSED";
                color = {intensity, 0.0, 0.0, 1.0};
                break;
        }
        ImGui::TextColored(color, ICON_FA_PLUG " %s", strstatus.c_str());
        ImGui::End();
    }
}

void UIController::info_widget(Scene *scene) {
    int width, height;
    glfwGetWindowSize(glfwGetCurrentContext(), &width, &height);
    const float desired_width = 300;
    ImGui::SetNextWindowPos({width - desired_width, 20}, ImGuiCond_Always);
    ImGui::SetNextWindowSize({desired_width, static_cast<float>(height - 20 - 30)}, ImGuiCond_Always);
    ImGui::Begin("Info", &wnd_->show_info, ImGuiWindowFlags_NoTitleBar);
    const auto flags = ImGuiTreeNodeFlags_DefaultOpen;
    if (ImGui::CollapsingHeader(ICON_FA_COGS " Settings")) {
        if (ImGui::CollapsingHeader(ICON_FA_VIDEO_CAMERA " Camera", flags)) {
            ImGui::PushItemWidth(150);
            ImGui::InputFloat2("Position", glm::value_ptr(camera_->pos_), 1);
            ImGui::InputFloat("Viewport size", &camera_->viewport_size_, 50.0, 1000.0, 0);
            ImGui::PopItemWidth();
        }
        if (ImGui::CollapsingHeader(ICON_FA_EYEDROPPER " Colors", flags)) {
            ImGui::SetColorEditOptions(ImGuiColorEditFlags_NoInputs);
            ImGui::ColorEdit3("Background", glm::value_ptr(conf_->ui.clear_color));
            ImGui::ColorEdit3("Grid", glm::value_ptr(conf_->scene.grid_color));
            ImGui::ColorEdit3("Canvas", glm::value_ptr(conf_->scene.scene_color));
        }
        if (ImGui::CollapsingHeader(ICON_FA_MAP_O " Options", flags)) {
            ImGui::Checkbox("World origin on top left", &conf_->camera.origin_on_top_left);
            ImGui::Checkbox("Draw grid", &conf_->scene.show_grid);
            static const ImVec4 button_colors[] = {
                ImVec4(0.5, 0.5, 0.5, 1.0),
                ImVec4(0.58, 0.941, 0.429, 1.0)
            };
            size_t idx = 0;
            static const std::array<const char *, static_cast<size_t>(Frame::LAYERS_COUNT)> captions{
                {"##layer0", "##layer1", "##layer2", "##layer3", "##layer4"}
            };
            for (bool &enabled : conf_->scene.enabled_layers) {
                if (ImGui::ColorButton(captions[idx], button_colors[enabled], ImGuiColorEditFlags_NoTooltip)) {
                    enabled = !enabled;
                }
                ++idx;
                ImGui::SameLine();
            }
            ImGui::LabelText("##layers", "%s", "Visible layers");
        }
    }
    if (ImGui::CollapsingHeader(ICON_FA_COMMENT_O " Frame message", flags)) {
        ImGui::BeginChild("FrameMsg", {0, 0}, true);
        ImGui::TextWrapped("%s", scene->get_frame_user_message());
        ImGui::EndChild();
    }
    ImGui::End();
}

void UIController::playback_control_widget(Scene *scene) {
    static const auto button_size = ImVec2{0, 0};
    static const float buttons_spacing = 5.0f;
    auto &io = ImGui::GetIO();
    auto width = io.DisplaySize.x;
    ImGui::SetNextWindowPos({0, io.DisplaySize.y - 20 - 2 * ImGui::GetStyle().WindowPadding.y});
    ImGui::SetNextWindowSize({width, 30});
    static const auto flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize
                              | ImGuiWindowFlags_NoMove
                              | ImGuiWindowFlags_NoSavedSettings;
    if (ImGui::Begin("Playback control", &wnd_->show_playback_control, flags)) {
        ImGui::BeginGroup();

        int tick = scene->get_frame_index();

        if (!io.WantTextInput) {
            if (io.KeyCtrl) {
                tick -= key_pressed_once(GLFW_KEY_LEFT);
                tick += key_pressed_once(GLFW_KEY_RIGHT);
            } else {
                tick -= io.KeysDown[GLFW_KEY_LEFT];
                tick += io.KeysDown[GLFW_KEY_RIGHT];
            }
        }

        ImGui::Button(ICON_FA_FAST_BACKWARD "##fastprev", button_size);
        if (ImGui::IsItemActive()) {
            tick -= conf_->ui.fast_skip_speed;
        }
        ImGui::SameLine(0.0f, buttons_spacing);

        if (ImGui::Button(ICON_FA_BACKWARD "##prev", button_size)) {
            --tick;
        }
        ImGui::SameLine(0.0f, buttons_spacing);

        if (autoplay_scene_) {
            autoplay_scene_ = !ImGui::Button(ICON_FA_PAUSE "##pause", button_size);
        } else {
            autoplay_scene_ = ImGui::Button(ICON_FA_PLAY "##play", button_size);
        }
        ImGui::SameLine(0.0f, buttons_spacing);

        if (ImGui::Button(ICON_FA_FORWARD "##next", button_size)) {
            ++tick;
        }
        ImGui::SameLine(0.0f, buttons_spacing);

        ImGui::Button(ICON_FA_FAST_FORWARD "##fastnext", button_size);
        if (ImGui::IsItemActive()) {
            tick += conf_->ui.fast_skip_speed;
        }
        ImGui::SameLine();

        tick += autoplay_scene_;

        const auto frames_cnt = scene->get_frames_count();
        if (frames_cnt > 0) {
            tick = std::min(tick, frames_cnt);
            float ftick = tick;
            ImGui::PushItemWidth(-1);
            if (io.KeyCtrl && io.KeysDown[GLFW_KEY_G]) {
                ImGui::SetKeyboardFocusHere();
            }
            if (ImGui::TickBar("##empty", &ftick, 0, frames_cnt, {0.0f, 0.0f})) {
                tick = static_cast<int>(ftick);
            }
            ImGui::PopItemWidth();
            scene->set_frame_index(tick);
        } else {
            ImGui::Text("Frame list empty");
        }

        ImGui::EndGroup();
        ImGui::End();
    }
}

bool UIController::key_pressed_once(int key_desc) {
    const auto &io = ImGui::GetIO();
    if (io.KeysDown[key_desc]) {
        if (!key_pressed_[key_desc]) {
            key_pressed_[key_desc] = true;
            return true;
        }
    } else {
        key_pressed_[key_desc] = false;
    }
    return false;
}
<commit_msg>Slight UI fixes<commit_after>//
// Created by valdemar on 14.10.17.
//

#include "UIController.h"

#include <imgui_impl/imgui_widgets.h>
#include <imgui_impl/imgui_impl_glfw_gl3.h>
#include <imgui_impl/style.h>
#include <fontawesome.h>

#include <glm/gtc/type_ptr.hpp>

struct UIController::wnd_t {
    bool show_style_editor = false;
    bool show_fps_overlay = true;
    bool show_info = true;
    bool show_playback_control = true;
    bool show_ui_help = false;
    bool show_shortcuts_help = false;
    bool show_metrics = false;
    bool show_mouse_pos_tooltip = false;
};

UIController::UIController(Camera *camera, Config *conf)
    : camera_(camera)
    , conf_(conf) {

    // Setup ImGui binding
    ImGui_ImplGlfwGL3_Init(glfwGetCurrentContext(), true);
    wnd_ = std::make_unique<wnd_t>();

    setup_custom_style(false);

    auto &io = ImGui::GetIO();

    io.IniFilename = "rewindviewer.ini";

    //Load and merge fontawesome to current font
    io.Fonts->AddFontDefault();
    const ImWchar icons_range[] = {ICON_MIN_FA, ICON_MAX_FA, 0};
    ImFontConfig icons_config;
    icons_config.MergeMode = true;
    icons_config.PixelSnapH = true;
    io.Fonts->AddFontFromFileTTF("resources/fonts/fontawesome-webfont.ttf", 14.0f, &icons_config, icons_range);
    //Need to call it here, otherwise fontawesome glyph ranges would be corrupted on Windows
    ImGui_ImplGlfwGL3_CreateDeviceObjects();
}

UIController::~UIController() {
    // Cleanup imgui
    ImGui_ImplGlfwGL3_Shutdown();
}

void UIController::next_frame(Scene *scene, NetListener::ConStatus client_status) {
    // Start new frame
    ImGui_ImplGlfwGL3_NewFrame();

    //Clear data option
    if (ImGui::BeginMainMenuBar()) {
        if (ImGui::BeginMenu(ICON_FA_PENCIL_SQUARE_O " Preferences", true)) {
            ImGui::Checkbox("Close window by Escape key", &conf_->ui.close_with_esc);
            ImGui::EndMenu();
        }
        ImGui::EndMainMenuBar();
    }

    //Update windows status
    main_menu_bar();

    if (wnd_->show_fps_overlay) {
        fps_overlay_widget(client_status);
    }
    if (wnd_->show_info) {
        info_widget(scene);
    }
    if (wnd_->show_playback_control) {
        playback_control_widget(scene);
    }
    if (wnd_->show_style_editor) {
        ImGui::Begin("Style editor", &wnd_->show_style_editor);
        ImGui::ShowStyleEditor();
        ImGui::End();
    }
    if (wnd_->show_metrics) {
        ImGui::ShowMetricsWindow(&wnd_->show_metrics);
    }
    if (wnd_->show_ui_help) {
        ImGui::Begin(ICON_FA_INFO_CIRCLE " UI guide", &wnd_->show_ui_help, ImGuiWindowFlags_AlwaysAutoResize);
        ImGui::ShowUserGuide();
        ImGui::End();
    }
    if (wnd_->show_shortcuts_help) {
        ImGui::Begin(ICON_FA_KEYBOARD_O " Controls help",
                     &wnd_->show_shortcuts_help,
                     ImGuiWindowFlags_AlwaysAutoResize);
        ImGui::BulletText("Mouse drag on map to move camera");
        ImGui::BulletText("Mouse wheel to zoom");
        ImGui::BulletText("Space - play/stop frame playback");
        ImGui::BulletText("Left, right arrow - manually change frames");
        ImGui::BulletText("Esc - close application");
        ImGui::BulletText("Ctrl+g - go to tick");
        ImGui::BulletText("g - Toggle grid draw state");
        ImGui::BulletText("p - Show tooltip with cursor world coordinates");
        ImGui::BulletText("1-5 - Toggle layers visibility");

        ImGui::End();
    }

    const auto &io = ImGui::GetIO();
    if (!io.WantCaptureMouse && wnd_->show_mouse_pos_tooltip) {
        ImGui::BeginTooltip();
        auto wmouse_pos = camera_->screen2world(io.MousePos);
        ImGui::Text("(%.3f, %.3f)", wmouse_pos.x, wmouse_pos.y);
        ImGui::EndTooltip();
    }

    //Checking hotkeys
    if (!io.WantTextInput) {
        if (key_pressed_once(GLFW_KEY_SPACE)) {
            autoplay_scene_ = !autoplay_scene_;
        }
        if (io.KeysDown[GLFW_KEY_LEFT]) {
            autoplay_scene_ = false;
        }
        if (key_pressed_once(GLFW_KEY_G) && !io.KeyCtrl) {
            conf_->scene.show_grid = !conf_->scene.show_grid;
        }
        if (key_pressed_once(GLFW_KEY_P)) {
            wnd_->show_mouse_pos_tooltip = !wnd_->show_mouse_pos_tooltip;
        }
        if (io.KeysDown[GLFW_KEY_D] && io.KeyCtrl) {
            developer_mode_ = true;
        }
        auto &enabled_layers = conf_->scene.enabled_layers;
        for (size_t layer_idx = 0; layer_idx < enabled_layers.size(); ++layer_idx) {
            if (key_pressed_once(static_cast<int>(GLFW_KEY_1 + layer_idx))) {
                enabled_layers[layer_idx] = !enabled_layers[layer_idx];
            }
        }
        if (scene->has_data() && io.KeysDown[GLFW_KEY_R] && io.KeyCtrl) {
            scene->clear_data(false);
        }
    }

    request_exit_ = conf_->ui.close_with_esc && io.KeysDown[GLFW_KEY_ESCAPE];

    //Hittest for detailed unit info
    if (!ImGui::GetIO().WantCaptureMouse) {
        scene->show_detailed_info(camera_->screen2world(ImGui::GetIO().MousePos));
    }

    //Background color
    glClearColor(conf_->ui.clear_color.r, conf_->ui.clear_color.g, conf_->ui.clear_color.b, 1.0f);
}

void UIController::frame_end() {
    ImGui::Render();
}

bool UIController::close_requested() {
    return request_exit_;
}

void UIController::main_menu_bar() {
    if (ImGui::BeginMainMenuBar()) {
        if (ImGui::BeginMenu(ICON_FA_EYE " View", true)) {
            ImGui::Checkbox("FPS overlay", &wnd_->show_fps_overlay);
            ImGui::Checkbox("Utility window", &wnd_->show_info);
            if (developer_mode_) {
                ImGui::Separator();
                ImGui::Checkbox("Style editor", &wnd_->show_style_editor);
                ImGui::Checkbox("Metrics", &wnd_->show_metrics);
            }
            ImGui::EndMenu();
        }
        if (ImGui::BeginMenu(ICON_FA_QUESTION_CIRCLE_O " Help", true)) {
            ImGui::MenuItem(ICON_FA_INFO_CIRCLE " UI guide", nullptr, &wnd_->show_ui_help);
            ImGui::MenuItem(ICON_FA_KEYBOARD_O " Controls", nullptr, &wnd_->show_shortcuts_help);
            ImGui::EndMenu();
        }
        ImGui::EndMainMenuBar();
    }
}

void UIController::fps_overlay_widget(NetListener::ConStatus net_status) {
    ImGui::SetNextWindowPos(ImVec2(10, 20));
    const auto flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize
                       | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings;
    if (ImGui::Begin("FPS Overlay", &wnd_->show_fps_overlay, ImVec2{0, 0}, 0.3, flags)) {
        ImGui::BeginGroup();
        ImGui::TextColored({1.0, 1.0, 0.0, 1.0}, "FPS %.1f", ImGui::GetIO().Framerate);
        ImGui::SameLine();
        ImGui::Text("[%.1f ms]", 1000.0f / ImGui::GetIO().Framerate);
        ImGui::EndGroup();
        std::string strstatus;
        ImVec4 color;
        static const float intensity = 1.0;
        switch (net_status) {
            case NetListener::ConStatus::WAIT:
                strstatus = "WAITING";
                color = {intensity, intensity, 0.0, 1.0};
                break;
            case NetListener::ConStatus::ESTABLISHED:
                strstatus = "CONNECTED";
                color = {0.0, intensity, 0.0, 1.0};
                break;
            case NetListener::ConStatus::CLOSED:
                strstatus = "CLOSED";
                color = {intensity, 0.0, 0.0, 1.0};
                break;
        }
        ImGui::TextColored(color, ICON_FA_PLUG " %s", strstatus.c_str());
        ImGui::End();
    }
}

void UIController::info_widget(Scene *scene) {
    int width, height;
    glfwGetWindowSize(glfwGetCurrentContext(), &width, &height);
    const float desired_width = 300;
    ImGui::SetNextWindowPos({width - desired_width, 20}, ImGuiCond_Always);
    ImGui::SetNextWindowSize({desired_width, static_cast<float>(height - 20 - 30)}, ImGuiCond_Always);
    ImGui::Begin("Info", &wnd_->show_info, ImGuiWindowFlags_NoTitleBar);
    const auto flags = ImGuiTreeNodeFlags_DefaultOpen;
    if (ImGui::CollapsingHeader(ICON_FA_COGS " Settings")) {
        if (ImGui::CollapsingHeader(ICON_FA_VIDEO_CAMERA " Camera", flags)) {
            ImGui::PushItemWidth(150);
            ImGui::InputFloat2("Position", glm::value_ptr(camera_->pos_), 1);
            ImGui::InputFloat("Viewport size", &camera_->viewport_size_, 50.0, 1000.0, 0);
            ImGui::PopItemWidth();
        }
        if (ImGui::CollapsingHeader(ICON_FA_EYEDROPPER " Colors", flags)) {
            ImGui::SetColorEditOptions(ImGuiColorEditFlags_NoInputs);
            ImGui::ColorEdit3("Background", glm::value_ptr(conf_->ui.clear_color));
            ImGui::ColorEdit3("Grid", glm::value_ptr(conf_->scene.grid_color));
            ImGui::ColorEdit3("Canvas", glm::value_ptr(conf_->scene.scene_color));
        }
        if (ImGui::CollapsingHeader(ICON_FA_MAP_O " Options", flags)) {
            ImGui::Checkbox("World origin on top left", &conf_->camera.origin_on_top_left);
            ImGui::Checkbox("Draw grid", &conf_->scene.show_grid);
            static const ImVec4 button_colors[] = {
                ImVec4(0.5, 0.5, 0.5, 1.0),
                ImVec4(0.58, 0.941, 0.429, 1.0)
            };
            size_t idx = 0;
            static const std::array<const char *, static_cast<size_t>(Frame::LAYERS_COUNT)> captions{
                {"##layer0", "##layer1", "##layer2", "##layer3", "##layer4"}
            };
            for (bool &enabled : conf_->scene.enabled_layers) {
                if (ImGui::ColorButton(captions[idx], button_colors[enabled], ImGuiColorEditFlags_NoTooltip)) {
                    enabled = !enabled;
                }
                ++idx;
                ImGui::SameLine();
            }
            ImGui::LabelText("##layers", "%s", "Visible layers");
        }
    }
    if (ImGui::CollapsingHeader(ICON_FA_COMMENT_O " Frame message", flags)) {
        ImGui::BeginChild("FrameMsg", {0, 0}, true);
        ImGui::TextWrapped("%s", scene->get_frame_user_message());
        ImGui::EndChild();
    }
    ImGui::End();
}

void UIController::playback_control_widget(Scene *scene) {
    static const auto button_size = ImVec2{0, 0};
    static const float buttons_spacing = 5.0f;
    auto &io = ImGui::GetIO();
    auto width = io.DisplaySize.x;
    ImGui::SetNextWindowPos({0, io.DisplaySize.y - 20 - 2 * ImGui::GetStyle().WindowPadding.y});
    ImGui::SetNextWindowSize({width, 30});
    static const auto flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize
                              | ImGuiWindowFlags_NoMove
                              | ImGuiWindowFlags_NoSavedSettings;
    if (ImGui::Begin("Playback control", &wnd_->show_playback_control, flags)) {
        ImGui::BeginGroup();

        //Tick is one indexed
        int tick = scene->get_frame_index() + 1;

        if (!io.WantTextInput) {
            if (io.KeyCtrl) {
                tick -= key_pressed_once(GLFW_KEY_LEFT);
                tick += key_pressed_once(GLFW_KEY_RIGHT);
            } else {
                tick -= io.KeysDown[GLFW_KEY_LEFT];
                tick += io.KeysDown[GLFW_KEY_RIGHT];
            }
        }

        ImGui::Button(ICON_FA_FAST_BACKWARD "##fastprev", button_size);
        if (ImGui::IsItemActive()) {
            tick -= conf_->ui.fast_skip_speed;
        }
        ImGui::SameLine(0.0f, buttons_spacing);

        if (ImGui::Button(ICON_FA_BACKWARD "##prev", button_size)) {
            --tick;
        }
        ImGui::SameLine(0.0f, buttons_spacing);

        if (autoplay_scene_) {
            autoplay_scene_ = !ImGui::Button(ICON_FA_PAUSE "##pause", button_size);
        } else {
            autoplay_scene_ = ImGui::Button(ICON_FA_PLAY "##play", button_size);
        }
        ImGui::SameLine(0.0f, buttons_spacing);

        if (ImGui::Button(ICON_FA_FORWARD "##next", button_size)) {
            ++tick;
        }
        ImGui::SameLine(0.0f, buttons_spacing);

        ImGui::Button(ICON_FA_FAST_FORWARD "##fastnext", button_size);
        if (ImGui::IsItemActive()) {
            tick += conf_->ui.fast_skip_speed;
        }
        ImGui::SameLine();

        tick += autoplay_scene_;

        const auto frames_cnt = scene->get_frames_count();
        if (frames_cnt > 0) {
            tick = cg::clamp(tick, 1, frames_cnt);
            float ftick = tick;
            ImGui::PushItemWidth(-1);
            if (io.KeyCtrl && io.KeysDown[GLFW_KEY_G]) {
                ImGui::SetKeyboardFocusHere();
            }
            if (ImGui::TickBar("##empty", &ftick, 0, frames_cnt, {0.0f, 0.0f})) {
                tick = static_cast<int>(ftick);
            }
            ImGui::PopItemWidth();
            scene->set_frame_index(tick - 1);
        } else {
            ImGui::Text("Frame list empty");
        }

        ImGui::EndGroup();
        ImGui::End();
    }
}

bool UIController::key_pressed_once(int key_desc) {
    const auto &io = ImGui::GetIO();
    if (io.KeysDown[key_desc]) {
        if (!key_pressed_[key_desc]) {
            key_pressed_[key_desc] = true;
            return true;
        }
    } else {
        key_pressed_[key_desc] = false;
    }
    return false;
}
<|endoftext|>


<commit_before>/*
 * NotebookPlots.cpp
 *
 * Copyright (C) 2009-19 by RStudio, PBC
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include "SessionRmdNotebook.hpp"
#include "NotebookPlots.hpp"
#include "NotebookOutput.hpp"
#include "../SessionPlots.hpp"

#include <boost/format.hpp>

#include <core/BoostSignals.hpp>
#include <core/Exec.hpp>
#include <core/StringUtils.hpp>

#include <core/system/FileMonitor.hpp>

#include <session/SessionModuleContext.hpp>

#include <r/RExec.hpp>
#include <r/RSexp.hpp>
#include <r/session/RGraphics.hpp>
#include <r/ROptions.hpp>
#include <r/RRoutines.hpp>

#define kPlotPrefix "_rs_chunk_plot_"
#define kGoldenRatio 1.618

using namespace rstudio::core;

namespace rstudio {
namespace session {
namespace modules {
namespace rmarkdown {
namespace notebook {
namespace {

bool isPlotPath(const FilePath& path)
{
   return path.hasExtensionLowerCase(".png") &&
          string_utils::isPrefixOf(path.getStem(), kPlotPrefix);
}

SEXP rs_recordExternalPlot(SEXP plotFilesSEXP)
{
   std::vector<std::string> plotFiles;
   if (r::sexp::fillVectorString(plotFilesSEXP, &plotFiles))
   {
      for (const std::string& plotFile : plotFiles)
      {
         if (plotFile.empty())
            continue;
         FilePath plot = module_context::resolveAliasedPath(plotFile);
         if (!plot.exists())
            continue;
         events().onPlotOutput(plot, FilePath(), json::Value(), 0);
      }
   }
   return R_NilValue;
}

} // anonymous namespace

PlotCapture::PlotCapture() :
   hasPlots_(false),
   plotPending_(false),
   lastOrdinal_(0)
{
}

PlotCapture::~PlotCapture()
{
}

void PlotCapture::processPlots(bool ignoreEmpty)
{
   // ensure plot folder exists
   if (!plotFolder_.exists())
      return;

   // collect plots from the folder
   std::vector<FilePath> folderContents;
   Error error = plotFolder_.getChildren(folderContents);
   if (error)
      LOG_ERROR(error);

   // sort entries by filename
   std::sort(
            folderContents.begin(),
            folderContents.end(),
            std::less<FilePath>());

   for (const FilePath& path : folderContents)
   {
      if (isPlotPath(path))
      {
         // we might find an empty plot file if it hasn't been flushed to disk
         // yet--ignore these
         if (ignoreEmpty && path.getSize() == 0)
            continue;

         // record height/width along with plot
         json::Object metadata;
         metadata["height"] = height_;
         metadata["width"] = width_;
         metadata["size_behavior"] = static_cast<int>(sizeBehavior_);

         // use cached conditions if we have them; otherwise, check accumulator
         if (conditions_.empty())
         {
            metadata["conditions"] = endConditionCapture();
         }
         else
         {
            metadata["conditions"] = conditions_.front();
            conditions_.pop_front();
         }

         // emit the plot and the snapshot file
         events().onPlotOutput(path, snapshotFile_, metadata, lastOrdinal_);

         // we've consumed the snapshot file, so clear it
         snapshotFile_ = FilePath();
         lastOrdinal_ = 0;

         // clean up the plot so it isn't emitted twice
         error = path.removeIfExists();
         if (error)
            LOG_ERROR(error);
      }
   }
}

void PlotCapture::saveSnapshot()
{
   // no work to do if we don't have a display list to write
   if (lastPlot_.isNil())
      return;

   // if there's a plot on the device, write its display list before it's
   // cleared for the next page
   FilePath outputFile = plotFolder_.completePath(
      core::system::generateUuid(false) + kDisplayListExt);

   Error error = r::exec::RFunction(
         ".rs.saveNotebookGraphics",
         lastPlot_.get(),
         string_utils::utf8ToSystem(outputFile.getAbsolutePath())).call();

   if (error)
      LOG_ERROR(error);
   else
      snapshotFile_ = outputFile;

   // clear the display list now that it's been written
   lastPlot_.releaseNow();
}

void PlotCapture::onExprComplete()
{
   r::sexp::Protect protect;

   // no action if no plots were created in this chunk
   if (!hasPlots_)
      return;

   // no action if nothing on device list (implies no graphics output)
   if (!isGraphicsDeviceActive())
      return;

   // finish capturing the conditions, if any
   if (capturingConditions())
      conditions_.push_back(endConditionCapture());

   // if we were expecting a new plot to be produced by the previous
   // expression, process the plot folder
   if (plotPending_)
   {
      plotPending_ = false;
      processPlots(true);
   }

   // check the current state of the graphics device against the last known
   // state
   SEXP plot = R_NilValue;
   Error error = r::exec::RFunction("recordPlot").call(&plot, &protect);
   if (error)
   {
      LOG_ERROR(error);
      return;
   }

   // detect changes and save last state
   bool unchanged = false;
   if (!lastPlot_.isNil())
      r::exec::RFunction("identical", plot, lastPlot_.get()).call(&unchanged);
   lastPlot_.set(plot);

   // if the state changed, reserve an ordinal at this position
   if (!unchanged)
   {
      OutputPair pair = lastChunkOutput(docId_, chunkId_, nbCtxId_);
      lastOrdinal_ = ++pair.ordinal;
      pair.outputType = ChunkOutputPlot;
      updateLastChunkOutput(docId_, chunkId_, pair);

      // notify the client so it can create a placeholder
      json::Object unit;
      unit[kChunkOutputType]    = static_cast<int>(ChunkOutputOrdinal);
      unit[kChunkOutputValue]   = static_cast<int>(lastOrdinal_);
      unit[kChunkOutputOrdinal] = static_cast<int>(lastOrdinal_);
      json::Object placeholder;
      placeholder[kChunkId]         = chunkId_;
      placeholder[kChunkDocId]      = docId_;
      placeholder[kChunkOutputPath] = unit;

      module_context::enqueClientEvent(ClientEvent(
               client_events::kChunkOutput, placeholder));
   }

}

void PlotCapture::removeGraphicsDevice()
{
   // take a snapshot of the last plot's display list before we turn off the
   // device (if we haven't emitted it yet)
   if (hasPlots_ &&
       sizeBehavior_ == PlotSizeAutomatic &&
       snapshotFile_.isEmpty())
      saveSnapshot();

   // turn off the graphics device, if it was ever turned on -- this has the
   // side effect of writing the device's remaining output to files
   if (isGraphicsDeviceActive())
   {
      Error error = r::exec::RFunction("dev.off").call();
      if (error)
         LOG_ERROR(error);

      // some operations may trigger the graphics device without actually
      // writing a plot; ignore these
      if (hasPlots_)
         processPlots(false);
   }
   hasPlots_ = false;
}

void PlotCapture::onBeforeNewPlot()
{
   if (!lastPlot_.isNil())
   {
      // save the snapshot of the plot to disk
      if (sizeBehavior_ == PlotSizeAutomatic)
         saveSnapshot();
   }
   beginConditionCapture();
   plotPending_ = true;
   hasPlots_ = true;
}

void PlotCapture::onNewPlot()
{
   beginConditionCapture();
   hasPlots_ = true;
   processPlots(true);
}

// begins capturing plot output
core::Error PlotCapture::connectPlots(const std::string& docId,
      const std::string& chunkId, const std::string& nbCtxId,
      double height, double width, PlotSizeBehavior sizeBehavior,
      const FilePath& plotFolder)
{
   // save identifiers
   docId_ = docId;
   chunkId_ = chunkId;
   nbCtxId_ = nbCtxId;

   // clean up any stale plots from the folder
   plotFolder_ = plotFolder;
   std::vector<FilePath> folderContents;
   Error error = plotFolder.getChildren(folderContents);
   if (error)
      return error;

   for (const core::FilePath& file : folderContents)
   {
      // remove if it looks like a plot
      if (isPlotPath(file))
      {
         error = file.remove();
         if (error)
         {
            // this is non-fatal
            LOG_ERROR(error);
         }
      }
   }

   // infer height/width if only one is given
   if (height == 0 && width > 0)
      height = width / kGoldenRatio;
   else if (height > 0 && width == 0)
      width = height * kGoldenRatio;
   width_ = width;
   height_ = height;
   sizeBehavior_ = sizeBehavior;

   // save old device option
   deviceOption_.set(r::options::getOption("device"));

   // set option for notebook graphics device (must succeed)
   error = setGraphicsOption();
   if (error)
      return error;

   onBeforeNewPlot_ = plots::events().onBeforeNewPlot.connect(
         boost::bind(&PlotCapture::onBeforeNewPlot, this));

   onBeforeNewGridPage_ = plots::events().onBeforeNewGridPage.connect(
         boost::bind(&PlotCapture::onBeforeNewPlot, this));

   onNewPlot_ = plots::events().onNewPlot.connect(
         boost::bind(&PlotCapture::onNewPlot, this));

   NotebookCapture::connect();
   return Success();
}

void PlotCapture::disconnect()
{
   if (connected())
   {
      // remove the graphics device if we created it
      removeGraphicsDevice();

      // restore the graphics device option
      r::options::setOption("device", deviceOption_.get());

      onNewPlot_.disconnect();
      onBeforeNewPlot_.disconnect();
      onBeforeNewGridPage_.disconnect();
   }
   NotebookCapture::disconnect();
}

core::Error PlotCapture::setGraphicsOption()
{
   Error error;

   // create the notebook graphics device
   r::exec::RFunction setOption(".rs.setNotebookGraphicsOption");

   // the folder in which to place the rendered plots (this is a sibling of the
   // main chunk output folder)
   setOption.addParam(
      string_utils::utf8ToSystem(plotFolder_.getAbsolutePath()) +
            "/" kPlotPrefix "%03d.png");

   // device dimensions
   setOption.addParam(height_);
   setOption.addParam(width_);

   // sizing behavior drives units -- user specified units are in inches but
   // we use pixels when scaling automatically
   setOption.addParam(sizeBehavior_ == PlotSizeManual ? "in" : "px");

   // device parameters
   setOption.addParam(r::session::graphics::device::devicePixelRatio());

   // other args (OS dependent)
   setOption.addParam(r::session::graphics::extraBitmapParams());

   return setOption.call();
}

bool PlotCapture::isGraphicsDeviceActive()
{
   r::sexp::Protect protect;
   SEXP devlist = R_NilValue;
   Error error = r::exec::RFunction("dev.list").call(&devlist, &protect);
   if (error)
      LOG_ERROR(error);
   if (r::sexp::isNull(devlist))
      return false;
   return true;
}

core::Error initPlots()
{
   RS_REGISTER_CALL_METHOD(rs_recordExternalPlot, 1);

   ExecBlock initBlock;
   initBlock.addFunctions()
      (boost::bind(module_context::sourceModuleRFile, "NotebookPlots.R"));

   return initBlock.execute();
}

} // namespace notebook
} // namespace rmarkdown
} // namespace modules
} // namespace session
} // namespace rstudio

<commit_msg>do not attempt to read device list after error<commit_after>/*
 * NotebookPlots.cpp
 *
 * Copyright (C) 2009-19 by RStudio, PBC
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include "SessionRmdNotebook.hpp"
#include "NotebookPlots.hpp"
#include "NotebookOutput.hpp"
#include "../SessionPlots.hpp"

#include <boost/format.hpp>

#include <core/BoostSignals.hpp>
#include <core/Exec.hpp>
#include <core/StringUtils.hpp>

#include <core/system/FileMonitor.hpp>

#include <session/SessionModuleContext.hpp>

#include <r/RExec.hpp>
#include <r/RSexp.hpp>
#include <r/session/RGraphics.hpp>
#include <r/ROptions.hpp>
#include <r/RRoutines.hpp>

#define kPlotPrefix "_rs_chunk_plot_"
#define kGoldenRatio 1.618

using namespace rstudio::core;

namespace rstudio {
namespace session {
namespace modules {
namespace rmarkdown {
namespace notebook {
namespace {

bool isPlotPath(const FilePath& path)
{
   return path.hasExtensionLowerCase(".png") &&
          string_utils::isPrefixOf(path.getStem(), kPlotPrefix);
}

SEXP rs_recordExternalPlot(SEXP plotFilesSEXP)
{
   std::vector<std::string> plotFiles;
   if (r::sexp::fillVectorString(plotFilesSEXP, &plotFiles))
   {
      for (const std::string& plotFile : plotFiles)
      {
         if (plotFile.empty())
            continue;
         FilePath plot = module_context::resolveAliasedPath(plotFile);
         if (!plot.exists())
            continue;
         events().onPlotOutput(plot, FilePath(), json::Value(), 0);
      }
   }
   return R_NilValue;
}

} // anonymous namespace

PlotCapture::PlotCapture() :
   hasPlots_(false),
   plotPending_(false),
   lastOrdinal_(0)
{
}

PlotCapture::~PlotCapture()
{
}

void PlotCapture::processPlots(bool ignoreEmpty)
{
   // ensure plot folder exists
   if (!plotFolder_.exists())
      return;

   // collect plots from the folder
   std::vector<FilePath> folderContents;
   Error error = plotFolder_.getChildren(folderContents);
   if (error)
      LOG_ERROR(error);

   // sort entries by filename
   std::sort(
            folderContents.begin(),
            folderContents.end(),
            std::less<FilePath>());

   for (const FilePath& path : folderContents)
   {
      if (isPlotPath(path))
      {
         // we might find an empty plot file if it hasn't been flushed to disk
         // yet--ignore these
         if (ignoreEmpty && path.getSize() == 0)
            continue;

         // record height/width along with plot
         json::Object metadata;
         metadata["height"] = height_;
         metadata["width"] = width_;
         metadata["size_behavior"] = static_cast<int>(sizeBehavior_);

         // use cached conditions if we have them; otherwise, check accumulator
         if (conditions_.empty())
         {
            metadata["conditions"] = endConditionCapture();
         }
         else
         {
            metadata["conditions"] = conditions_.front();
            conditions_.pop_front();
         }

         // emit the plot and the snapshot file
         events().onPlotOutput(path, snapshotFile_, metadata, lastOrdinal_);

         // we've consumed the snapshot file, so clear it
         snapshotFile_ = FilePath();
         lastOrdinal_ = 0;

         // clean up the plot so it isn't emitted twice
         error = path.removeIfExists();
         if (error)
            LOG_ERROR(error);
      }
   }
}

void PlotCapture::saveSnapshot()
{
   // no work to do if we don't have a display list to write
   if (lastPlot_.isNil())
      return;

   // if there's a plot on the device, write its display list before it's
   // cleared for the next page
   FilePath outputFile = plotFolder_.completePath(
      core::system::generateUuid(false) + kDisplayListExt);

   Error error = r::exec::RFunction(
         ".rs.saveNotebookGraphics",
         lastPlot_.get(),
         string_utils::utf8ToSystem(outputFile.getAbsolutePath())).call();

   if (error)
      LOG_ERROR(error);
   else
      snapshotFile_ = outputFile;

   // clear the display list now that it's been written
   lastPlot_.releaseNow();
}

void PlotCapture::onExprComplete()
{
   r::sexp::Protect protect;

   // no action if no plots were created in this chunk
   if (!hasPlots_)
      return;

   // no action if nothing on device list (implies no graphics output)
   if (!isGraphicsDeviceActive())
      return;

   // finish capturing the conditions, if any
   if (capturingConditions())
      conditions_.push_back(endConditionCapture());

   // if we were expecting a new plot to be produced by the previous
   // expression, process the plot folder
   if (plotPending_)
   {
      plotPending_ = false;
      processPlots(true);
   }

   // check the current state of the graphics device against the last known
   // state
   SEXP plot = R_NilValue;
   Error error = r::exec::RFunction("recordPlot").call(&plot, &protect);
   if (error)
   {
      LOG_ERROR(error);
      return;
   }

   // detect changes and save last state
   bool unchanged = false;
   if (!lastPlot_.isNil())
      r::exec::RFunction("identical", plot, lastPlot_.get()).call(&unchanged);
   lastPlot_.set(plot);

   // if the state changed, reserve an ordinal at this position
   if (!unchanged)
   {
      OutputPair pair = lastChunkOutput(docId_, chunkId_, nbCtxId_);
      lastOrdinal_ = ++pair.ordinal;
      pair.outputType = ChunkOutputPlot;
      updateLastChunkOutput(docId_, chunkId_, pair);

      // notify the client so it can create a placeholder
      json::Object unit;
      unit[kChunkOutputType]    = static_cast<int>(ChunkOutputOrdinal);
      unit[kChunkOutputValue]   = static_cast<int>(lastOrdinal_);
      unit[kChunkOutputOrdinal] = static_cast<int>(lastOrdinal_);
      json::Object placeholder;
      placeholder[kChunkId]         = chunkId_;
      placeholder[kChunkDocId]      = docId_;
      placeholder[kChunkOutputPath] = unit;

      module_context::enqueClientEvent(ClientEvent(
               client_events::kChunkOutput, placeholder));
   }

}

void PlotCapture::removeGraphicsDevice()
{
   // take a snapshot of the last plot's display list before we turn off the
   // device (if we haven't emitted it yet)
   if (hasPlots_ &&
       sizeBehavior_ == PlotSizeAutomatic &&
       snapshotFile_.isEmpty())
      saveSnapshot();

   // turn off the graphics device, if it was ever turned on -- this has the
   // side effect of writing the device's remaining output to files
   if (isGraphicsDeviceActive())
   {
      Error error = r::exec::RFunction("dev.off").call();
      if (error)
         LOG_ERROR(error);

      // some operations may trigger the graphics device without actually
      // writing a plot; ignore these
      if (hasPlots_)
         processPlots(false);
   }
   hasPlots_ = false;
}

void PlotCapture::onBeforeNewPlot()
{
   if (!lastPlot_.isNil())
   {
      // save the snapshot of the plot to disk
      if (sizeBehavior_ == PlotSizeAutomatic)
         saveSnapshot();
   }
   beginConditionCapture();
   plotPending_ = true;
   hasPlots_ = true;
}

void PlotCapture::onNewPlot()
{
   beginConditionCapture();
   hasPlots_ = true;
   processPlots(true);
}

// begins capturing plot output
core::Error PlotCapture::connectPlots(const std::string& docId,
      const std::string& chunkId, const std::string& nbCtxId,
      double height, double width, PlotSizeBehavior sizeBehavior,
      const FilePath& plotFolder)
{
   // save identifiers
   docId_ = docId;
   chunkId_ = chunkId;
   nbCtxId_ = nbCtxId;

   // clean up any stale plots from the folder
   plotFolder_ = plotFolder;
   std::vector<FilePath> folderContents;
   Error error = plotFolder.getChildren(folderContents);
   if (error)
      return error;

   for (const core::FilePath& file : folderContents)
   {
      // remove if it looks like a plot
      if (isPlotPath(file))
      {
         error = file.remove();
         if (error)
         {
            // this is non-fatal
            LOG_ERROR(error);
         }
      }
   }

   // infer height/width if only one is given
   if (height == 0 && width > 0)
      height = width / kGoldenRatio;
   else if (height > 0 && width == 0)
      width = height * kGoldenRatio;
   width_ = width;
   height_ = height;
   sizeBehavior_ = sizeBehavior;

   // save old device option
   deviceOption_.set(r::options::getOption("device"));

   // set option for notebook graphics device (must succeed)
   error = setGraphicsOption();
   if (error)
      return error;

   onBeforeNewPlot_ = plots::events().onBeforeNewPlot.connect(
         boost::bind(&PlotCapture::onBeforeNewPlot, this));

   onBeforeNewGridPage_ = plots::events().onBeforeNewGridPage.connect(
         boost::bind(&PlotCapture::onBeforeNewPlot, this));

   onNewPlot_ = plots::events().onNewPlot.connect(
         boost::bind(&PlotCapture::onNewPlot, this));

   NotebookCapture::connect();
   return Success();
}

void PlotCapture::disconnect()
{
   if (connected())
   {
      // remove the graphics device if we created it
      removeGraphicsDevice();

      // restore the graphics device option
      r::options::setOption("device", deviceOption_.get());

      onNewPlot_.disconnect();
      onBeforeNewPlot_.disconnect();
      onBeforeNewGridPage_.disconnect();
   }
   NotebookCapture::disconnect();
}

core::Error PlotCapture::setGraphicsOption()
{
   Error error;

   // create the notebook graphics device
   r::exec::RFunction setOption(".rs.setNotebookGraphicsOption");

   // the folder in which to place the rendered plots (this is a sibling of the
   // main chunk output folder)
   setOption.addParam(
      string_utils::utf8ToSystem(plotFolder_.getAbsolutePath()) +
            "/" kPlotPrefix "%03d.png");

   // device dimensions
   setOption.addParam(height_);
   setOption.addParam(width_);

   // sizing behavior drives units -- user specified units are in inches but
   // we use pixels when scaling automatically
   setOption.addParam(sizeBehavior_ == PlotSizeManual ? "in" : "px");

   // device parameters
   setOption.addParam(r::session::graphics::device::devicePixelRatio());

   // other args (OS dependent)
   setOption.addParam(r::session::graphics::extraBitmapParams());

   return setOption.call();
}

bool PlotCapture::isGraphicsDeviceActive()
{
   r::sexp::Protect protect;
   SEXP devlist = R_NilValue;
   Error error = r::exec::RFunction("dev.list").call(&devlist, &protect);
   if (error)
   {
      // We have seen AV crashes reading the contents of the devlist pointer, so if there's an error
      // log it and presume the graphics device is not active. The result will be that we don't
      // capture plots in this chunk since our graphics device doesn't appear to be engaged.
      LOG_ERROR(error);
      return false;
   }
   if (r::sexp::isNull(devlist))
      return false;
   return true;
}

core::Error initPlots()
{
   RS_REGISTER_CALL_METHOD(rs_recordExternalPlot, 1);

   ExecBlock initBlock;
   initBlock.addFunctions()
      (boost::bind(module_context::sourceModuleRFile, "NotebookPlots.R"));

   return initBlock.execute();
}

} // namespace notebook
} // namespace rmarkdown
} // namespace modules
} // namespace session
} // namespace rstudio

<|endoftext|>


<commit_before>#include "RdYarpNetworkManager.hpp"

//-- Initialize static members
rd::RdYarpNetworkManager * rd::RdYarpNetworkManager::uniqueInstance = NULL;
const std::string rd::RdYarpNetworkManager::id = "YARP";

bool rd::RdYarpNetworkManager::RegisterManager()
{
    if (uniqueInstance == NULL)
    {
        uniqueInstance = new RdYarpNetworkManager();
    }

    return Register( uniqueInstance, id);
}

rd::RdYarpNetworkManager::~RdYarpNetworkManager()
{
    uniqueInstance = NULL;
}

bool rd::RdYarpNetworkManager::start()
{
    if (player_id == -1)
    {
        RD_ERROR("NetworkManager not initialized, player id not set\n");
        return false;
    }

    yarp::os::NetworkBase::initMinimum();

    //-- Open the rcpClient port with this player's id
    std::ostringstream rpc_s;
    rpc_s << "/";
    rpc_s << id;
    rpc_s << "/rdServer/rpc:o";
    rpcClient.open( rpc_s.str().c_str() );
    //-- Open the callback port with this player's id
    std::ostringstream call_s;
    call_s << "/";
    call_s << id;
    call_s << "/rdServer/command:i";
    callbackPort.open( call_s.str().c_str());

    //-- Try to connect to the server until timeout
    int tries = 0;
    while(tries++ < 10)
    {
        if(rpcClient.getOutputCount() > 0)
            break;
        RD_INFO("Waiting for rpc to be connected to server...\n");
        yarp::os::Time::delay(0.5);
        yarp::os::Network::connect( rpc_s.str().c_str() , "/rdServer" );
    }

    if (tries == 10)
    {
        RD_ERROR("Timeout for rpc to be connected to server.\n");
        return false;
    }

    RD_SUCCESS("Rpc connected to Server (outgoing)!\n")

    if ( !yarp::os::Network::connect( "/rdBroadcast", call_s.str().c_str() ))
    {
        RD_ERROR("Error connecting to server (incoming broadcast).\n");
        return false;
    }

    RD_SUCCESS("Connected to Server (incoming broadcast)!\n")

    callbackPort.useCallback(*this);

    return true;
}

void rd::RdYarpNetworkManager::onRead(yarp::os::Bottle &b)
{
    //RD_INFO("Got %s\n", b.toString().c_str());
    if ((b.get(0).asString() == "players")||(b.get(0).asVocab() == VOCAB_RD_PLAYERS)) {  // players //
        //RD_INFO("Number of players: %d\n",b.size()-1);  // -1 because of vocab.
        std::vector< RdPlayer > players;
        for(size_t i=1;i<b.size();i++)
        {

            RdPlayer rdPlayer(b.get(i).asList()->get(0).asInt(),
                              b.get(i).asList()->get(1).asString().c_str(),
                              b.get(i).asList()->get(2).asInt(),
                              b.get(i).asList()->get(3).asInt(),
                              b.get(i).asList()->get(4).asInt(),
                              b.get(i).asList()->get(5).asInt()
                              );
             players.push_back(rdPlayer);
        }

        //-- Notify listeners
        for(int i = 0; i < listeners.size(); i++)
            listeners[i]->onDataArrived(players);
    }
    else
    {
        RD_ERROR("What?\n");
    }

}

rd::RdYarpNetworkManager::RdYarpNetworkManager()
{
    player_id = -1;
}

bool rd::RdYarpNetworkManager::onTargetHit(rd::RdTarget target, rd::RdPlayer player, rd::RdWeapon weapon)
{
    return sendPlayerHit(player, weapon.getDamage());
}

bool rd::RdYarpNetworkManager::stop()
{
    rpcClient.close();

     callbackPort.disableCallback();
     callbackPort.interrupt();
     callbackPort.close();

     yarp::os::NetworkBase::finiMinimum();

     return true;
}

bool rd::RdYarpNetworkManager::configure(std::string parameter, std::string value)
{
    if (parameter.compare("player_id") == 0)
    {
        player_id = atoi(value.c_str());
    }

    return RdNetworkManager::configure(parameter, value);
}

bool rd::RdYarpNetworkManager::sendPlayerHit(rd::RdPlayer player, int damage)
{
    //-- Send a message to the server with the player Id and the damage done:
    yarp::os::Bottle msg_player_hit, response;
    msg_player_hit.addVocab(VOCAB_RD_HIT);
    msg_player_hit.addInt(player.getId());
    msg_player_hit.addInt(damage);
    rpcClient.write(msg_player_hit,response);
    RD_INFO("rdServer response from hit: %s\n",response.toString().c_str());

    //-- Check response
    if ( strcmp( response.toString().c_str(), "[ok]") == 0)
        return true;
    else
        return false;
}

bool rd::RdYarpNetworkManager::login(rd::RdPlayer player)
{
    //-- Start network system
    std::stringstream ss;
    ss << player.getId();
    configure("player_id", ss.str());

    if( !start())
    {
        RD_ERROR("RdNetworkManager could not be started for player %d", player.getId() );
        return false;
    }

    //-- Send login message
    yarp::os::Bottle msgRdPlayer,res;
    msgRdPlayer.addVocab(VOCAB_RD_LOGIN);
    msgRdPlayer.addInt(player.getId());
    msgRdPlayer.addString(player.getName().c_str());
    msgRdPlayer.addInt(player.getTeamId());
    rpcClient.write(msgRdPlayer,res);
    RD_INFO("rdServer response from login: %s\n",res.toString().c_str());

    //-- Check response
    if ( strcmp( res.toString().c_str(), "[ok]") == 0)
        return true;
    else
        return false;
}

bool rd::RdYarpNetworkManager::logout(rd::RdPlayer player)
{
    RD_INFO("Logout...\n");
    yarp::os::Bottle msgRdPlayer,res;
    msgRdPlayer.addVocab(VOCAB_RD_LOGOUT);
    msgRdPlayer.addInt(player.getId());
    rpcClient.write(msgRdPlayer,res);
    RD_INFO("rdServer response from logout: %s\n",res.toString().c_str());

    //-- Check response
    if ( strcmp( res.toString().c_str(), "[ok]") == 0)
    {
        RD_SUCCESS("Logout ok\n");
        return true;
    }
    else
    {
        RD_ERROR("Logout failed\n");
        return false;
    }
}
<commit_msg>drop unneeded conversion<commit_after>#include "RdYarpNetworkManager.hpp"

//-- Initialize static members
rd::RdYarpNetworkManager * rd::RdYarpNetworkManager::uniqueInstance = NULL;
const std::string rd::RdYarpNetworkManager::id = "YARP";

bool rd::RdYarpNetworkManager::RegisterManager()
{
    if (uniqueInstance == NULL)
    {
        uniqueInstance = new RdYarpNetworkManager();
    }

    return Register( uniqueInstance, id);
}

rd::RdYarpNetworkManager::~RdYarpNetworkManager()
{
    uniqueInstance = NULL;
}

bool rd::RdYarpNetworkManager::start()
{
    if (player_id == -1)
    {
        RD_ERROR("NetworkManager not initialized, player id not set\n");
        return false;
    }

    yarp::os::NetworkBase::initMinimum();

    //-- Open the rcpClient port with this player's id
    std::ostringstream rpc_s;
    rpc_s << "/";
    rpc_s << id;
    rpc_s << "/rdServer/rpc:o";
    rpcClient.open( rpc_s.str().c_str() );
    //-- Open the callback port with this player's id
    std::ostringstream call_s;
    call_s << "/";
    call_s << id;
    call_s << "/rdServer/command:i";
    callbackPort.open( call_s.str().c_str());

    //-- Try to connect to the server until timeout
    int tries = 0;
    while(tries++ < 10)
    {
        if(rpcClient.getOutputCount() > 0)
            break;
        RD_INFO("Waiting for rpc to be connected to server...\n");
        yarp::os::Time::delay(0.5);
        yarp::os::Network::connect( rpc_s.str() , "/rdServer" );
    }

    if (tries == 10)
    {
        RD_ERROR("Timeout for rpc to be connected to server.\n");
        return false;
    }

    RD_SUCCESS("Rpc connected to Server (outgoing)!\n")

    if ( !yarp::os::Network::connect( "/rdBroadcast", call_s.str() ))
    {
        RD_ERROR("Error connecting to server (incoming broadcast).\n");
        return false;
    }

    RD_SUCCESS("Connected to Server (incoming broadcast)!\n")

    callbackPort.useCallback(*this);

    return true;
}

void rd::RdYarpNetworkManager::onRead(yarp::os::Bottle &b)
{
    //RD_INFO("Got %s\n", b.toString().c_str());
    if ((b.get(0).asString() == "players")||(b.get(0).asVocab() == VOCAB_RD_PLAYERS)) {  // players //
        //RD_INFO("Number of players: %d\n",b.size()-1);  // -1 because of vocab.
        std::vector< RdPlayer > players;
        for(size_t i=1;i<b.size();i++)
        {

            RdPlayer rdPlayer(b.get(i).asList()->get(0).asInt(),
                              b.get(i).asList()->get(1).asString().c_str(),
                              b.get(i).asList()->get(2).asInt(),
                              b.get(i).asList()->get(3).asInt(),
                              b.get(i).asList()->get(4).asInt(),
                              b.get(i).asList()->get(5).asInt()
                              );
             players.push_back(rdPlayer);
        }

        //-- Notify listeners
        for(int i = 0; i < listeners.size(); i++)
            listeners[i]->onDataArrived(players);
    }
    else
    {
        RD_ERROR("What?\n");
    }

}

rd::RdYarpNetworkManager::RdYarpNetworkManager()
{
    player_id = -1;
}

bool rd::RdYarpNetworkManager::onTargetHit(rd::RdTarget target, rd::RdPlayer player, rd::RdWeapon weapon)
{
    return sendPlayerHit(player, weapon.getDamage());
}

bool rd::RdYarpNetworkManager::stop()
{
    rpcClient.close();

     callbackPort.disableCallback();
     callbackPort.interrupt();
     callbackPort.close();

     yarp::os::NetworkBase::finiMinimum();

     return true;
}

bool rd::RdYarpNetworkManager::configure(std::string parameter, std::string value)
{
    if (parameter.compare("player_id") == 0)
    {
        player_id = atoi(value.c_str());
    }

    return RdNetworkManager::configure(parameter, value);
}

bool rd::RdYarpNetworkManager::sendPlayerHit(rd::RdPlayer player, int damage)
{
    //-- Send a message to the server with the player Id and the damage done:
    yarp::os::Bottle msg_player_hit, response;
    msg_player_hit.addVocab(VOCAB_RD_HIT);
    msg_player_hit.addInt(player.getId());
    msg_player_hit.addInt(damage);
    rpcClient.write(msg_player_hit,response);
    RD_INFO("rdServer response from hit: %s\n",response.toString().c_str());

    //-- Check response
    if ( strcmp( response.toString().c_str(), "[ok]") == 0)
        return true;
    else
        return false;
}

bool rd::RdYarpNetworkManager::login(rd::RdPlayer player)
{
    //-- Start network system
    std::stringstream ss;
    ss << player.getId();
    configure("player_id", ss.str());

    if( !start())
    {
        RD_ERROR("RdNetworkManager could not be started for player %d", player.getId() );
        return false;
    }

    //-- Send login message
    yarp::os::Bottle msgRdPlayer,res;
    msgRdPlayer.addVocab(VOCAB_RD_LOGIN);
    msgRdPlayer.addInt(player.getId());
    msgRdPlayer.addString(player.getName().c_str());
    msgRdPlayer.addInt(player.getTeamId());
    rpcClient.write(msgRdPlayer,res);
    RD_INFO("rdServer response from login: %s\n",res.toString().c_str());

    //-- Check response
    if ( strcmp( res.toString().c_str(), "[ok]") == 0)
        return true;
    else
        return false;
}

bool rd::RdYarpNetworkManager::logout(rd::RdPlayer player)
{
    RD_INFO("Logout...\n");
    yarp::os::Bottle msgRdPlayer,res;
    msgRdPlayer.addVocab(VOCAB_RD_LOGOUT);
    msgRdPlayer.addInt(player.getId());
    rpcClient.write(msgRdPlayer,res);
    RD_INFO("rdServer response from logout: %s\n",res.toString().c_str());

    //-- Check response
    if ( strcmp( res.toString().c_str(), "[ok]") == 0)
    {
        RD_SUCCESS("Logout ok\n");
        return true;
    }
    else
    {
        RD_ERROR("Logout failed\n");
        return false;
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2018-present ScyllaDB
 */

/*
 * SPDX-License-Identifier: AGPL-3.0-or-later
 */

#include <seastar/util/defer.hh>
#include <boost/range/adaptor/map.hpp>
#include "view_update_generator.hh"
#include "service/priority_manager.hh"
#include "utils/error_injection.hh"
#include "db/view/view_updating_consumer.hh"
#include "sstables/sstables.hh"
#include "readers/evictable.hh"

static logging::logger vug_logger("view_update_generator");

static inline void inject_failure(std::string_view operation) {
    utils::get_local_injector().inject(operation,
            [operation] { throw std::runtime_error(std::string(operation)); });
}

namespace db::view {

future<> view_update_generator::start() {
    thread_attributes attr;
    attr.sched_group = _db.get_streaming_scheduling_group();
    _started = seastar::async(std::move(attr), [this]() mutable {
        auto drop_sstable_references = defer([&] () noexcept {
            // Clear sstable references so sstables_manager::stop() doesn't hang.
            vug_logger.info("leaving {} unstaged sstables unprocessed",
                    _sstables_to_move.size(), _sstables_with_tables.size());
            _sstables_to_move.clear();
            _sstables_with_tables.clear();
        });
        while (!_as.abort_requested()) {
            if (_sstables_with_tables.empty()) {
                _pending_sstables.wait().get();
            }

            // To ensure we don't race with updates, move the entire content
            // into a local variable.
            auto sstables_with_tables = std::exchange(_sstables_with_tables, {});

            // If we got here, we will process all tables we know about so far eventually so there
            // is no starvation
            for (auto table_it = sstables_with_tables.begin(); table_it != sstables_with_tables.end(); table_it = sstables_with_tables.erase(table_it)) {
                auto& [t, sstables] = *table_it;
                schema_ptr s = t->schema();

                vug_logger.trace("Processing {}.{}: {} sstables", s->ks_name(), s->cf_name(), sstables.size());

                const auto num_sstables = sstables.size();

                try {
                    // Exploit the fact that sstables in the staging directory
                    // are usually non-overlapping and use a partitioned set for
                    // the read.
                    auto ssts = make_lw_shared<sstables::sstable_set>(sstables::make_partitioned_sstable_set(s, false));
                    for (auto& sst : sstables) {
                        ssts->insert(sst);
                    }

                    auto permit = _db.obtain_reader_permit(*t, "view_update_generator", db::no_timeout).get0();
                    auto ms = mutation_source([this, ssts] (
                                schema_ptr s,
                                reader_permit permit,
                                const dht::partition_range& pr,
                                const query::partition_slice& ps,
                                const io_priority_class& pc,
                                tracing::trace_state_ptr ts,
                                streamed_mutation::forwarding fwd_ms,
                                mutation_reader::forwarding fwd_mr) {
                        return ssts->make_range_sstable_reader(s, std::move(permit), pr, ps, pc, std::move(ts), fwd_ms, fwd_mr);
                    });
                    auto [staging_sstable_reader, staging_sstable_reader_handle] = make_manually_paused_evictable_reader_v2(
                            std::move(ms),
                            s,
                            permit,
                            query::full_partition_range,
                            s->full_slice(),
                            service::get_local_streaming_priority(),
                            nullptr,
                            ::mutation_reader::forwarding::no);

                    inject_failure("view_update_generator_consume_staging_sstable");
                    auto result = staging_sstable_reader.consume_in_thread(view_updating_consumer(s, std::move(permit), *t, sstables, _as, staging_sstable_reader_handle));
                    staging_sstable_reader.close().get();
                    if (result == stop_iteration::yes) {
                        break;
                    }
                } catch (...) {
                    vug_logger.warn("Processing {} failed for table {}:{}. Will retry...", s->ks_name(), s->cf_name(), std::current_exception());
                    // Need to add sstables back to the set so we can retry later. By now it may
                    // have had other updates.
                    std::move(sstables.begin(), sstables.end(), std::back_inserter(_sstables_with_tables[t]));
                    break;
                }
                try {
                    inject_failure("view_update_generator_collect_consumed_sstables");
                    // collect all staging sstables to move in a map, grouped by table.
                    std::move(sstables.begin(), sstables.end(), std::back_inserter(_sstables_to_move[t]));
                } catch (...) {
                    // Move from staging will be retried upon restart.
                    vug_logger.warn("Moving {} from staging failed: {}:{}. Ignoring...", s->ks_name(), s->cf_name(), std::current_exception());
                }
                _registration_sem.signal(num_sstables);
            }
            // For each table, move the processed staging sstables into the table's base dir.
            for (auto it = _sstables_to_move.begin(); it != _sstables_to_move.end(); ) {
                auto& [t, sstables] = *it;
                try {
                    inject_failure("view_update_generator_move_staging_sstable");
                    t->move_sstables_from_staging(sstables).get();
                } catch (...) {
                    // Move from staging will be retried upon restart.
                    vug_logger.warn("Moving some sstable from staging failed: {}. Ignoring...", std::current_exception());
                }
                it = _sstables_to_move.erase(it);
            }
        }
    });
    return make_ready_future<>();
}

future<> view_update_generator::stop() {
    _as.request_abort();
    _pending_sstables.signal();
    return std::move(_started).then([this] {
        _registration_sem.broken();
    });
}

bool view_update_generator::should_throttle() const {
    return !_started.available();
}

future<> view_update_generator::register_staging_sstable(sstables::shared_sstable sst, lw_shared_ptr<replica::table> table) {
    if (_as.abort_requested()) {
        return make_ready_future<>();
    }
    inject_failure("view_update_generator_registering_staging_sstable");
    _sstables_with_tables[table].push_back(std::move(sst));

    _pending_sstables.signal();
    if (should_throttle()) {
        return _registration_sem.wait(1);
    } else {
        _registration_sem.consume(1);
        return make_ready_future<>();
    }
}

void view_update_generator::setup_metrics() {
    namespace sm = seastar::metrics;

    _metrics.add_group("view_update_generator", {
        sm::make_gauge("pending_registrations", sm::description("Number of tasks waiting to register staging sstables"),
                [this] { return _registration_sem.waiters(); }),

        sm::make_gauge("queued_batches_count",
                sm::description("Number of sets of sstables queued for view update generation"),
                [this] { return _sstables_with_tables.size(); }),

        sm::make_gauge("sstables_to_move_count",
                sm::description("Number of sets of sstables which are already processed and wait to be moved from their staging directory"),
                [this] { return _sstables_to_move.size(); })
    });
}

void view_update_generator::discover_staging_sstables() {
    for (auto& x : _db.get_column_families()) {
        auto t = x.second->shared_from_this();
        const auto& sstables = t->get_staging_sstables();
        _sstables_with_tables[t].reserve(_sstables_with_tables[t].size() + sstables.size());
        for (auto& sst : sstables | boost::adaptors::map_values) {
            // FIXME: indentation
                _sstables_with_tables[t].push_back(sst);
                // we're at early stage here, no need to kick _pending_sstables (the
                // bulding fiber is not running), neither we can wait on the semaphore
                _registration_sem.consume(1);
        }
    }
}

}
<commit_msg>view_update_generator: discover_staging_sstables: reindent<commit_after>/*
 * Copyright (C) 2018-present ScyllaDB
 */

/*
 * SPDX-License-Identifier: AGPL-3.0-or-later
 */

#include <seastar/util/defer.hh>
#include <boost/range/adaptor/map.hpp>
#include "view_update_generator.hh"
#include "service/priority_manager.hh"
#include "utils/error_injection.hh"
#include "db/view/view_updating_consumer.hh"
#include "sstables/sstables.hh"
#include "readers/evictable.hh"

static logging::logger vug_logger("view_update_generator");

static inline void inject_failure(std::string_view operation) {
    utils::get_local_injector().inject(operation,
            [operation] { throw std::runtime_error(std::string(operation)); });
}

namespace db::view {

future<> view_update_generator::start() {
    thread_attributes attr;
    attr.sched_group = _db.get_streaming_scheduling_group();
    _started = seastar::async(std::move(attr), [this]() mutable {
        auto drop_sstable_references = defer([&] () noexcept {
            // Clear sstable references so sstables_manager::stop() doesn't hang.
            vug_logger.info("leaving {} unstaged sstables unprocessed",
                    _sstables_to_move.size(), _sstables_with_tables.size());
            _sstables_to_move.clear();
            _sstables_with_tables.clear();
        });
        while (!_as.abort_requested()) {
            if (_sstables_with_tables.empty()) {
                _pending_sstables.wait().get();
            }

            // To ensure we don't race with updates, move the entire content
            // into a local variable.
            auto sstables_with_tables = std::exchange(_sstables_with_tables, {});

            // If we got here, we will process all tables we know about so far eventually so there
            // is no starvation
            for (auto table_it = sstables_with_tables.begin(); table_it != sstables_with_tables.end(); table_it = sstables_with_tables.erase(table_it)) {
                auto& [t, sstables] = *table_it;
                schema_ptr s = t->schema();

                vug_logger.trace("Processing {}.{}: {} sstables", s->ks_name(), s->cf_name(), sstables.size());

                const auto num_sstables = sstables.size();

                try {
                    // Exploit the fact that sstables in the staging directory
                    // are usually non-overlapping and use a partitioned set for
                    // the read.
                    auto ssts = make_lw_shared<sstables::sstable_set>(sstables::make_partitioned_sstable_set(s, false));
                    for (auto& sst : sstables) {
                        ssts->insert(sst);
                    }

                    auto permit = _db.obtain_reader_permit(*t, "view_update_generator", db::no_timeout).get0();
                    auto ms = mutation_source([this, ssts] (
                                schema_ptr s,
                                reader_permit permit,
                                const dht::partition_range& pr,
                                const query::partition_slice& ps,
                                const io_priority_class& pc,
                                tracing::trace_state_ptr ts,
                                streamed_mutation::forwarding fwd_ms,
                                mutation_reader::forwarding fwd_mr) {
                        return ssts->make_range_sstable_reader(s, std::move(permit), pr, ps, pc, std::move(ts), fwd_ms, fwd_mr);
                    });
                    auto [staging_sstable_reader, staging_sstable_reader_handle] = make_manually_paused_evictable_reader_v2(
                            std::move(ms),
                            s,
                            permit,
                            query::full_partition_range,
                            s->full_slice(),
                            service::get_local_streaming_priority(),
                            nullptr,
                            ::mutation_reader::forwarding::no);

                    inject_failure("view_update_generator_consume_staging_sstable");
                    auto result = staging_sstable_reader.consume_in_thread(view_updating_consumer(s, std::move(permit), *t, sstables, _as, staging_sstable_reader_handle));
                    staging_sstable_reader.close().get();
                    if (result == stop_iteration::yes) {
                        break;
                    }
                } catch (...) {
                    vug_logger.warn("Processing {} failed for table {}:{}. Will retry...", s->ks_name(), s->cf_name(), std::current_exception());
                    // Need to add sstables back to the set so we can retry later. By now it may
                    // have had other updates.
                    std::move(sstables.begin(), sstables.end(), std::back_inserter(_sstables_with_tables[t]));
                    break;
                }
                try {
                    inject_failure("view_update_generator_collect_consumed_sstables");
                    // collect all staging sstables to move in a map, grouped by table.
                    std::move(sstables.begin(), sstables.end(), std::back_inserter(_sstables_to_move[t]));
                } catch (...) {
                    // Move from staging will be retried upon restart.
                    vug_logger.warn("Moving {} from staging failed: {}:{}. Ignoring...", s->ks_name(), s->cf_name(), std::current_exception());
                }
                _registration_sem.signal(num_sstables);
            }
            // For each table, move the processed staging sstables into the table's base dir.
            for (auto it = _sstables_to_move.begin(); it != _sstables_to_move.end(); ) {
                auto& [t, sstables] = *it;
                try {
                    inject_failure("view_update_generator_move_staging_sstable");
                    t->move_sstables_from_staging(sstables).get();
                } catch (...) {
                    // Move from staging will be retried upon restart.
                    vug_logger.warn("Moving some sstable from staging failed: {}. Ignoring...", std::current_exception());
                }
                it = _sstables_to_move.erase(it);
            }
        }
    });
    return make_ready_future<>();
}

future<> view_update_generator::stop() {
    _as.request_abort();
    _pending_sstables.signal();
    return std::move(_started).then([this] {
        _registration_sem.broken();
    });
}

bool view_update_generator::should_throttle() const {
    return !_started.available();
}

future<> view_update_generator::register_staging_sstable(sstables::shared_sstable sst, lw_shared_ptr<replica::table> table) {
    if (_as.abort_requested()) {
        return make_ready_future<>();
    }
    inject_failure("view_update_generator_registering_staging_sstable");
    _sstables_with_tables[table].push_back(std::move(sst));

    _pending_sstables.signal();
    if (should_throttle()) {
        return _registration_sem.wait(1);
    } else {
        _registration_sem.consume(1);
        return make_ready_future<>();
    }
}

void view_update_generator::setup_metrics() {
    namespace sm = seastar::metrics;

    _metrics.add_group("view_update_generator", {
        sm::make_gauge("pending_registrations", sm::description("Number of tasks waiting to register staging sstables"),
                [this] { return _registration_sem.waiters(); }),

        sm::make_gauge("queued_batches_count",
                sm::description("Number of sets of sstables queued for view update generation"),
                [this] { return _sstables_with_tables.size(); }),

        sm::make_gauge("sstables_to_move_count",
                sm::description("Number of sets of sstables which are already processed and wait to be moved from their staging directory"),
                [this] { return _sstables_to_move.size(); })
    });
}

void view_update_generator::discover_staging_sstables() {
    for (auto& x : _db.get_column_families()) {
        auto t = x.second->shared_from_this();
        const auto& sstables = t->get_staging_sstables();
        _sstables_with_tables[t].reserve(_sstables_with_tables[t].size() + sstables.size());
        for (auto& sst : sstables | boost::adaptors::map_values) {
            _sstables_with_tables[t].push_back(sst);
            // we're at early stage here, no need to kick _pending_sstables (the
            // bulding fiber is not running), neither we can wait on the semaphore
            _registration_sem.consume(1);
        }
    }
}

}
<|endoftext|>


<commit_before>/*
 * View3D_2.cpp
 *
 * Copyright (C) 2018 by VISUS (Universitaet Stuttgart).
 * Alle Rechte vorbehalten.
 */

#include "stdafx.h"
#include "mmcore/view/View3D_2.h"
#include "vislib/graphics/gl/IncludeAllGL.h"
#ifdef _WIN32
#    include <windows.h>
#endif /* _WIN32 */
#include "mmcore/CoreInstance.h"
#include "mmcore/misc/PngBitmapCodec.h"
#include "mmcore/param/BoolParam.h"
#include "mmcore/param/ButtonParam.h"
#include "mmcore/param/EnumParam.h"
#include "mmcore/param/FilePathParam.h"
#include "mmcore/param/FloatParam.h"
#include "mmcore/param/StringParam.h"
#include "mmcore/param/Vector3fParam.h"
#include "mmcore/utility/ColourParser.h"
#include "mmcore/view/CallRender3D_2.h"
#include "mmcore/view/CallRenderView.h" // TODO new call?
#include "mmcore/view/CameraParamOverride.h"
#include "vislib/Exception.h"
#include "vislib/String.h"
#include "vislib/StringSerialiser.h"
#include "vislib/Trace.h"
#include "vislib/graphics/BitmapImage.h"
#include "vislib/graphics/CameraParamsStore.h"
#include "vislib/math/Point.h"
#include "vislib/math/Quaternion.h"
#include "vislib/math/Vector.h"
#include "vislib/math/mathfunctions.h"
#include "vislib/sys/KeyCode.h"
#include "vislib/sys/Log.h"
#include "vislib/sys/sysfunctions.h"

#include "mmcore/utility/gl/Texture2D.h"

using namespace megamol::core;
using namespace megamol::core::view;

/*
 * View3D_2::View3D_2
 */
View3D_2::View3D_2(void)
    : view::AbstractView3D()
    , AbstractCamParamSync()
    , cursor2d()
    , rendererSlot("rendering", "Connects the view to a Renderer")
    , lightDir(0.5f, -1.0f, -1.0f)
    , isCamLight(true)
    , bboxs()
    , showBBox("showBBox", "Bool parameter to show/hide the bounding box")
    , showLookAt("showLookAt", "Flag showing the look at point")
    , cameraSettingsSlot("camsettings", "The stored camera settings")
    , storeCameraSettingsSlot("storecam", "Triggers the storage of the camera settings")
    , restoreCameraSettingsSlot("restorecam", "Triggers the restore of the camera settings")
    , resetViewSlot("resetView", "Triggers the reset of the view")
    , firstImg(false)
    , isCamLightSlot(
          "light::isCamLight", "Flag whether the light is relative to the camera or to the world coordinate system")
    , lightDirSlot("light::direction", "Direction vector of the light")
    , lightColDifSlot("light::diffuseCol", "Diffuse light colour")
    , lightColAmbSlot("light::ambientCol", "Ambient light colour")
    , stereoFocusDistSlot("stereo::focusDist", "focus distance for stereo projection")
    , stereoEyeDistSlot("stereo::eyeDist", "eye distance for stereo projection")
    , overrideCall(NULL)
    , viewKeyMoveStepSlot("viewKey::MoveStep", "The move step size in world coordinates")
    , viewKeyRunFactorSlot("viewKey::RunFactor", "The factor for step size multiplication when running (shift)")
    , viewKeyAngleStepSlot("viewKey::AngleStep", "The angle rotate step in degrees")
    , mouseSensitivitySlot("viewKey::MouseSensitivity", "used for WASD mode")
    , viewKeyRotPointSlot("viewKey::RotPoint", "The point around which the view will be roateted")
    , viewKeyRotLeftSlot("viewKey::RotLeft", "Rotates the view to the left (around the up-axis)")
    , viewKeyRotRightSlot("viewKey::RotRight", "Rotates the view to the right (around the up-axis)")
    , viewKeyRotUpSlot("viewKey::RotUp", "Rotates the view to the top (around the right-axis)")
    , viewKeyRotDownSlot("viewKey::RotDown", "Rotates the view to the bottom (around the right-axis)")
    , viewKeyRollLeftSlot("viewKey::RollLeft", "Rotates the view counter-clockwise (around the view-axis)")
    , viewKeyRollRightSlot("viewKey::RollRight", "Rotates the view clockwise (around the view-axis)")
    , viewKeyZoomInSlot("viewKey::ZoomIn", "Zooms in (moves the camera)")
    , viewKeyZoomOutSlot("viewKey::ZoomOut", "Zooms out (moves the camera)")
    , viewKeyMoveLeftSlot("viewKey::MoveLeft", "Moves to the left")
    , viewKeyMoveRightSlot("viewKey::MoveRight", "Moves to the right")
    , viewKeyMoveUpSlot("viewKey::MoveUp", "Moves to the top")
    , viewKeyMoveDownSlot("viewKey::MoveDown", "Moves to the bottom")
    , toggleBBoxSlot("toggleBBox", "Button to toggle the bounding box")
    , bboxCol{1.0f, 1.0f, 1.0f, 0.625f}
    , bboxColSlot("bboxCol", "Sets the colour for the bounding box")
    , enableMouseSelectionSlot("enableMouseSelection", "Enable selecting and picking with the mouse")
    , showViewCubeSlot("viewcube::show", "Shows the view cube helper")
    , resetViewOnBBoxChangeSlot("resetViewOnBBoxChange", "whether to reset the view when the bounding boxes change")
    , mouseX(0.0f)
    , mouseY(0.0f)
    , mouseFlags(0)
    , timeCtrl()
    , toggleMouseSelection(false)
    , hookOnChangeOnlySlot("hookOnChange", "whether post-hooks are triggered when the frame would be identical") {

    using vislib::sys::KeyCode;

    // TODO implement

    this->rendererSlot.SetCompatibleCall<CallRender3D_2Description>();
    this->MakeSlotAvailable(&this->rendererSlot);

    // TODO implement
}

/*
 * View3D_2::~View3D_2
 */
View3D_2::~View3D_2(void) {
    this->Release();
    this->overrideCall = nullptr; // DO NOT DELETE
}

/*
 * View3D_2::GetCameraSyncNumber
 */
unsigned int view::View3D_2::GetCameraSyncNumber(void) const {
    // TODO implement
    return 0;
}

/*
 * View3D_2::SerialiseCamera
 */
void view::View3D_2::SerialiseCamera(vislib::Serialiser& serialiser) const {
    // TODO implement
}

/*
 * View3D_2::DeserialiseCamera
 */
void view::View3D_2::DeserialiseCamera(vislib::Serialiser& serialiser) {
    // TODO implement
}

/*
 * View3D_2::Render
 */
void View3D_2::Render(const mmcRenderViewContext& context) {
    const float* bkgndCol = (this->overrideBkgndCol != nullptr) ? this->overrideBkgndCol : this->bkgndColour();
    glClearColor(bkgndCol[0], bkgndCol[1], bkgndCol[2], 0.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}

/*
 * View3D_2::ResetView
 */
void View3D_2::ResetView(void) {
    // TODO implement
}

/*
 * View3D_2::Resize
 */
void View3D_2::Resize(unsigned int width, unsigned int height) {
    // TODO implement
}

/*
 * View3D_2::SetCursor2DButtonState
 */
void View3D_2::SetCursor2DButtonState(unsigned int btn, bool down) {
    // TODO implement
}

/*
 * View3D_2::SetCursor2DPosition
 */
void View3D_2::SetCursor2DPosition(float x, float y) {
    // TODO implement
}

/*
 * View3D_2::SetInputModifier
 */
void View3D_2::SetInputModifier(mmcInputModifier mod, bool down) {
    // TODO implement
}

/*
 * View3D_2::OnRenderView
 */
bool View3D_2::OnRenderView(Call& call) {
    // TODO implement
    return true;
}

/*
 * View3D_2::UpdateFreeze
 */
void View3D_2::UpdateFreeze(bool freeze) {
    // intentionally empty?
}

/*
 * View3D_2::unpackMouseCoordinates
 */
void View3D_2::unpackMouseCoordinates(float& x, float& y) {
    // TODO implement
}

/*
 * View3D_2::create
 */
bool View3D_2::create(void) {
    // TODO the vislib shaders have to die a slow and painful death
    vislib::graphics::gl::ShaderSource lineVertSrc;
    vislib::graphics::gl::ShaderSource lineFragSrc;
    if (!this->GetCoreInstance()->ShaderSourceFactory().MakeShaderSource("lines::vertex", lineVertSrc)) {
        vislib::sys::Log::DefaultLog.WriteError("Unable to load vertex shader source for bounding box line shader");
    }
    if (!this->GetCoreInstance()->ShaderSourceFactory().MakeShaderSource("lines::fragment", lineFragSrc)) {
        vislib::sys::Log::DefaultLog.WriteError("Unable to load fragment shader source for bounding box line shader");
    }
    try {
        if (!this->lineShader.Create(
                lineVertSrc.Code(), lineVertSrc.Count(), lineFragSrc.Code(), lineFragSrc.Count())) {
            throw vislib::Exception("Shader creation failure", __FILE__, __LINE__);
        }
    } catch (vislib::Exception e) {
        vislib::sys::Log::DefaultLog.WriteError("Unable to create bounding box line shader: %s\n", e.GetMsgA());
        return false;
    }

    // TODO implement

    return true;
}

/*
 * View3D_2::release
 */
void View3D_2::release(void) { this->removeTitleRenderer(); }

/*
 * View3D_2::mouseSensitivityChanged
 */
bool View3D_2::mouseSensitivityChanged(param::ParamSlot& p) { return true; }

/*
 * View3D_2::renderBBox
 */
void View3D_2::renderBBox(void) {
    // TODO implement
}

/*
 * View3D_2::renderBBoxBackside
 */
void View3D_2::renderBBoxBackside(void) {
    // TODO implement
}

/*
 * View3D_2::renderBBoxFrontside
 */
void View3D_2::renderBBoxFrontside(void) {
    // TODO implement
}

/*
 * View3D_2::renderLookAt
 */
void View3D_2::renderLookAt(void) {
    // TODO implement
}

/*
 * View3D_2::OnGetCamParams
 */
bool View3D_2::OnGetCamParams(CallCamParamSync& c) {
    // TODO implement
    return true;
}

/*
 * View3D_2::onStoreCamera
 */
bool View3D_2::onStoreCamera(param::ParamSlot& p) {
    // TODO implement
    return true;
}

/*
 * View3D_2::onRestoreCamera
 */
bool View3D_2::onRestoreCamera(param::ParamSlot& p) {
    // TODO implement
    return true;
}

/*
 * View3D_2::onResetView
 */
bool View3D_2::onResetView(param::ParamSlot& p) {
    this->ResetView();
    return true;
}

/*
 * View3D_2::viewKeyPressed
 */
bool View3D_2::viewKeyPressed(param::ParamSlot& p) {
    // TODO implement
    return true;
}

/*
 * View3D_2::onToggleButton
 */
bool View3D_2::onToggleButton(param::ParamSlot& p) {
    // TODO implement
    return true;
}

/*
 * View3D_2::renderViewCube
 */
void View3D_2::renderViewCube(void) {
    // TODO implement
}
<commit_msg>made the new view compile again<commit_after>/*
 * View3D_2.cpp
 *
 * Copyright (C) 2018 by VISUS (Universitaet Stuttgart).
 * Alle Rechte vorbehalten.
 */

#include "stdafx.h"
#include "mmcore/view/View3D_2.h"
#include "vislib/graphics/gl/IncludeAllGL.h"
#ifdef _WIN32
#    include <windows.h>
#endif /* _WIN32 */
#include "mmcore/CoreInstance.h"
#include "mmcore/misc/PngBitmapCodec.h"
#include "mmcore/param/BoolParam.h"
#include "mmcore/param/ButtonParam.h"
#include "mmcore/param/EnumParam.h"
#include "mmcore/param/FilePathParam.h"
#include "mmcore/param/FloatParam.h"
#include "mmcore/param/StringParam.h"
#include "mmcore/param/Vector3fParam.h"
#include "mmcore/utility/ColourParser.h"
#include "mmcore/view/CallRender3D_2.h"
#include "mmcore/view/CallRenderView.h" // TODO new call?
#include "mmcore/view/CameraParamOverride.h"
#include "vislib/Exception.h"
#include "vislib/String.h"
#include "vislib/StringSerialiser.h"
#include "vislib/Trace.h"
#include "vislib/graphics/BitmapImage.h"
#include "vislib/graphics/CameraParamsStore.h"
#include "vislib/math/Point.h"
#include "vislib/math/Quaternion.h"
#include "vislib/math/Vector.h"
#include "vislib/math/mathfunctions.h"
#include "vislib/sys/KeyCode.h"
#include "vislib/sys/Log.h"
#include "vislib/sys/sysfunctions.h"

#include "mmcore/utility/gl/Texture2D.h"

using namespace megamol::core;
using namespace megamol::core::view;

/*
 * View3D_2::View3D_2
 */
View3D_2::View3D_2(void)
    : view::AbstractView3D()
    , AbstractCamParamSync()
    , cursor2d()
    , rendererSlot("rendering", "Connects the view to a Renderer")
    , lightDir(0.5f, -1.0f, -1.0f)
    , isCamLight(true)
    , bboxs()
    , showBBox("showBBox", "Bool parameter to show/hide the bounding box")
    , showLookAt("showLookAt", "Flag showing the look at point")
    , cameraSettingsSlot("camsettings", "The stored camera settings")
    , storeCameraSettingsSlot("storecam", "Triggers the storage of the camera settings")
    , restoreCameraSettingsSlot("restorecam", "Triggers the restore of the camera settings")
    , resetViewSlot("resetView", "Triggers the reset of the view")
    , firstImg(false)
    , isCamLightSlot(
          "light::isCamLight", "Flag whether the light is relative to the camera or to the world coordinate system")
    , lightDirSlot("light::direction", "Direction vector of the light")
    , lightColDifSlot("light::diffuseCol", "Diffuse light colour")
    , lightColAmbSlot("light::ambientCol", "Ambient light colour")
    , stereoFocusDistSlot("stereo::focusDist", "focus distance for stereo projection")
    , stereoEyeDistSlot("stereo::eyeDist", "eye distance for stereo projection")
    , overrideCall(NULL)
    , viewKeyMoveStepSlot("viewKey::MoveStep", "The move step size in world coordinates")
    , viewKeyRunFactorSlot("viewKey::RunFactor", "The factor for step size multiplication when running (shift)")
    , viewKeyAngleStepSlot("viewKey::AngleStep", "The angle rotate step in degrees")
    , mouseSensitivitySlot("viewKey::MouseSensitivity", "used for WASD mode")
    , viewKeyRotPointSlot("viewKey::RotPoint", "The point around which the view will be roateted")
    , viewKeyRotLeftSlot("viewKey::RotLeft", "Rotates the view to the left (around the up-axis)")
    , viewKeyRotRightSlot("viewKey::RotRight", "Rotates the view to the right (around the up-axis)")
    , viewKeyRotUpSlot("viewKey::RotUp", "Rotates the view to the top (around the right-axis)")
    , viewKeyRotDownSlot("viewKey::RotDown", "Rotates the view to the bottom (around the right-axis)")
    , viewKeyRollLeftSlot("viewKey::RollLeft", "Rotates the view counter-clockwise (around the view-axis)")
    , viewKeyRollRightSlot("viewKey::RollRight", "Rotates the view clockwise (around the view-axis)")
    , viewKeyZoomInSlot("viewKey::ZoomIn", "Zooms in (moves the camera)")
    , viewKeyZoomOutSlot("viewKey::ZoomOut", "Zooms out (moves the camera)")
    , viewKeyMoveLeftSlot("viewKey::MoveLeft", "Moves to the left")
    , viewKeyMoveRightSlot("viewKey::MoveRight", "Moves to the right")
    , viewKeyMoveUpSlot("viewKey::MoveUp", "Moves to the top")
    , viewKeyMoveDownSlot("viewKey::MoveDown", "Moves to the bottom")
    , toggleBBoxSlot("toggleBBox", "Button to toggle the bounding box")
    , bboxCol{1.0f, 1.0f, 1.0f, 0.625f}
    , bboxColSlot("bboxCol", "Sets the colour for the bounding box")
    , enableMouseSelectionSlot("enableMouseSelection", "Enable selecting and picking with the mouse")
    , showViewCubeSlot("viewcube::show", "Shows the view cube helper")
    , resetViewOnBBoxChangeSlot("resetViewOnBBoxChange", "whether to reset the view when the bounding boxes change")
    , mouseX(0.0f)
    , mouseY(0.0f)
    , mouseFlags(0)
    , timeCtrl()
    , toggleMouseSelection(false)
    , hookOnChangeOnlySlot("hookOnChange", "whether post-hooks are triggered when the frame would be identical") {

    using vislib::sys::KeyCode;

    // TODO implement

    this->rendererSlot.SetCompatibleCall<CallRender3D_2Description>();
    this->MakeSlotAvailable(&this->rendererSlot);

    // TODO implement
}

/*
 * View3D_2::~View3D_2
 */
View3D_2::~View3D_2(void) {
    this->Release();
    this->overrideCall = nullptr; // DO NOT DELETE
}

/*
 * View3D_2::GetCameraSyncNumber
 */
unsigned int view::View3D_2::GetCameraSyncNumber(void) const {
    // TODO implement
    return 0;
}

/*
 * View3D_2::SerialiseCamera
 */
void view::View3D_2::SerialiseCamera(vislib::Serialiser& serialiser) const {
    // TODO implement
}

/*
 * View3D_2::DeserialiseCamera
 */
void view::View3D_2::DeserialiseCamera(vislib::Serialiser& serialiser) {
    // TODO implement
}

/*
 * View3D_2::Render
 */
void View3D_2::Render(const mmcRenderViewContext& context) {
    const float* bkgndCol = (this->overrideBkgndCol != nullptr) ? this->overrideBkgndCol : this->BkgndColour();
    glClearColor(bkgndCol[0], bkgndCol[1], bkgndCol[2], 0.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}

/*
 * View3D_2::ResetView
 */
void View3D_2::ResetView(void) {
    // TODO implement
}

/*
 * View3D_2::Resize
 */
void View3D_2::Resize(unsigned int width, unsigned int height) {
    // TODO implement
}

/*
 * View3D_2::SetCursor2DButtonState
 */
void View3D_2::SetCursor2DButtonState(unsigned int btn, bool down) {
    // TODO implement
}

/*
 * View3D_2::SetCursor2DPosition
 */
void View3D_2::SetCursor2DPosition(float x, float y) {
    // TODO implement
}

/*
 * View3D_2::SetInputModifier
 */
void View3D_2::SetInputModifier(mmcInputModifier mod, bool down) {
    // TODO implement
}

/*
 * View3D_2::OnRenderView
 */
bool View3D_2::OnRenderView(Call& call) {
    // TODO implement
    return true;
}

/*
 * View3D_2::UpdateFreeze
 */
void View3D_2::UpdateFreeze(bool freeze) {
    // intentionally empty?
}

/*
 * View3D_2::unpackMouseCoordinates
 */
void View3D_2::unpackMouseCoordinates(float& x, float& y) {
    // TODO implement
}

/*
 * View3D_2::create
 */
bool View3D_2::create(void) {
    // TODO the vislib shaders have to die a slow and painful death
    vislib::graphics::gl::ShaderSource lineVertSrc;
    vislib::graphics::gl::ShaderSource lineFragSrc;
    if (!this->GetCoreInstance()->ShaderSourceFactory().MakeShaderSource("lines::vertex", lineVertSrc)) {
        vislib::sys::Log::DefaultLog.WriteError("Unable to load vertex shader source for bounding box line shader");
    }
    if (!this->GetCoreInstance()->ShaderSourceFactory().MakeShaderSource("lines::fragment", lineFragSrc)) {
        vislib::sys::Log::DefaultLog.WriteError("Unable to load fragment shader source for bounding box line shader");
    }
    try {
        if (!this->lineShader.Create(
                lineVertSrc.Code(), lineVertSrc.Count(), lineFragSrc.Code(), lineFragSrc.Count())) {
            throw vislib::Exception("Shader creation failure", __FILE__, __LINE__);
        }
    } catch (vislib::Exception e) {
        vislib::sys::Log::DefaultLog.WriteError("Unable to create bounding box line shader: %s\n", e.GetMsgA());
        return false;
    }

    // TODO implement

    return true;
}

/*
 * View3D_2::release
 */
void View3D_2::release(void) { this->removeTitleRenderer(); }

/*
 * View3D_2::mouseSensitivityChanged
 */
bool View3D_2::mouseSensitivityChanged(param::ParamSlot& p) { return true; }

/*
 * View3D_2::renderBBox
 */
void View3D_2::renderBBox(void) {
    // TODO implement
}

/*
 * View3D_2::renderBBoxBackside
 */
void View3D_2::renderBBoxBackside(void) {
    // TODO implement
}

/*
 * View3D_2::renderBBoxFrontside
 */
void View3D_2::renderBBoxFrontside(void) {
    // TODO implement
}

/*
 * View3D_2::renderLookAt
 */
void View3D_2::renderLookAt(void) {
    // TODO implement
}

/*
 * View3D_2::OnGetCamParams
 */
bool View3D_2::OnGetCamParams(CallCamParamSync& c) {
    // TODO implement
    return true;
}

/*
 * View3D_2::onStoreCamera
 */
bool View3D_2::onStoreCamera(param::ParamSlot& p) {
    // TODO implement
    return true;
}

/*
 * View3D_2::onRestoreCamera
 */
bool View3D_2::onRestoreCamera(param::ParamSlot& p) {
    // TODO implement
    return true;
}

/*
 * View3D_2::onResetView
 */
bool View3D_2::onResetView(param::ParamSlot& p) {
    this->ResetView();
    return true;
}

/*
 * View3D_2::viewKeyPressed
 */
bool View3D_2::viewKeyPressed(param::ParamSlot& p) {
    // TODO implement
    return true;
}

/*
 * View3D_2::onToggleButton
 */
bool View3D_2::onToggleButton(param::ParamSlot& p) {
    // TODO implement
    return true;
}

/*
 * View3D_2::renderViewCube
 */
void View3D_2::renderViewCube(void) {
    // TODO implement
}
<|endoftext|>