#pragma once #include "monitor/utils/plugins/plugin.hpp" namespace monitor::utils { struct raw_plugin : public plugin { public: enum class TYPE { RAW_DATA, MAGNITUDE, ENERGY }; public: hr::result m_result; TYPE m_type; std::string m_display_name; public: struct graph { public: // градуировка осьи X hr::fvec_t m_ox; // максимальное значение по оси Y double m_max_element = 0.0; // размер данных дял отрисовки std::size_t m_size = 0; // кол-во линий графика std::size_t m_line_count = 0; // тут кол-во графиков на массив данных из этих графиков т.е.: // [1] = [1, 2, ..., 1'000'000'000] // [2] = [1, 2, ..., 1'000'000'000] std::vector m_data; // говорит нужно ли делать сжатие графика ли нет bool m_is_scale = false; void fill_ox(std::size_t start_pos = 0) { m_ox.clear(); for (std::size_t i = start_pos; i < start_pos + m_size; ++i) m_ox.push_back(i); } void init() { m_data.reserve(m_line_count); m_ox.reserve(m_size); for (std::size_t i = 0; i < m_line_count; ++i) m_data.push_back(hr::fvec_t(m_size, 0.f)); } } m_graph; public: bool empty() { return m_result.empty(); } void graph_init() { try { auto raw_size = m_result.size(); if (raw_size == 0) throw std::invalid_argument("Error set data in plugin: empty data"); m_graph.m_is_scale = raw_size > var::MAX_RENDER_SIZE; m_graph.m_size = std::min(raw_size, var::MAX_RENDER_SIZE); m_graph.m_line_count = m_result.m_data.size(); m_graph.init(); m_graph.fill_ox(); } catch(std::exception& e) { hack::error()(e.what()); } } // этот метод запускается один раз при первом рендеринге // для заполнения начальными данными void fill() { if (m_graph.m_is_scale) { m_step = m_result.size() / m_graph.m_size + 1; std::size_t line_count = 0; for (auto& gd : m_graph.m_data) { std::size_t index = 0; for (auto& g : gd) { float tmp_e = 0.f; for (std::size_t j = index - m_step; j < index; ++j) { auto e = m_result.m_data[line_count][j].m_value; tmp_e = hack::math::max_abs(e, tmp_e); } g = tmp_e; m_graph.m_max_element = std::fabs(hack::math::max_abs(g, m_graph.m_max_element)); index += m_step; if (index > m_result.size()) index = m_result.size(); } ++line_count; } } else { // заполняется, когда данных пришло меньше чем нужно для полного рендеринга std::size_t graph_count = 0; for (auto el : m_result.m_data) { std::size_t index = 0; for (auto e : el) { m_graph.m_max_element = hack::math::max(e.m_value, m_graph.m_max_element); m_graph.m_data[graph_count][index] = e.m_value; ++index; } ++graph_count; } } } std::size_t m_local_k2 = 0; std::size_t m_step; bool is_scale() { return m_graph.m_is_scale; } // Это основной меод вычисления данных, которые нужно отрисовать // 1. Проверяем размер сырых данных // - если их больше чем var::MAX_RENDER_SIZE, то делаем сжатие // - если их меньше, то отрисовка идет полностью и fill_ox уже заполнен так как надо и при масштабировании ни чего не пересчитывается is_scale void fill(ImPlotRect current_limits) { // кол-во данных, которые мы сейчас хотим отрисовать, когда поменяли масштаб auto total_dots_for_render = current_limits.Size().x; // тоже, что начальный m_step, но если m_step меняется, то этот постоянный // т.е. это максимальный коефиниент сужения графика std::size_t k1 = m_result.size() / m_graph.m_size + 1; // на сколько изменилось кол-во точек, которые нужно отрисовать std::size_t k2 = var::MAX_RENDER_SIZE / total_dots_for_render - 1.f; // стэк условий, котолрые ограничивают лишние расчеты и сохраняют текущие данные if (k1 <= k2 && k2 != 0) return; if (m_local_k2 == k2 && k2 != 0) return; m_local_k2 = k2; // сколько сместилось точке отрисовки во время масштабирования за экран (влево) std::size_t skip_dots = current_limits.Min().x; // кол-во точек, которые нужно пропустить из сырых данных чтобы они не были отрисованы во время масштабирования std::size_t pos = skip_dots * (m_step + k2); // шаг пропуска, по которому делаем сужение графика m_step = k1 - k2; std::size_t line_count = 0; for (auto& gd : m_graph.m_data) { std::size_t index = pos; for (auto& g : gd) { float tmp_e = 0.f; for (std::size_t j = index - m_step; j < index; ++j) { auto e = m_result.m_data[line_count][j].m_value; // основной критерий сужения: берем максимальный элемент из данного интервала m_step tmp_e = hack::math::max_abs(e, tmp_e); } g = tmp_e; m_graph.m_max_element = std::fabs(hack::math::max_abs(g, m_graph.m_max_element)); index += m_step; if (index > m_result.size()) index = m_result.size(); } ++line_count; } // нужно передать смещение для установки градации m_graph.fill_ox(skip_dots); } }; }