aboutsummaryrefslogtreecommitdiff
path: root/core/client.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'core/client.cpp')
-rw-r--r--core/client.cpp366
1 files changed, 258 insertions, 108 deletions
diff --git a/core/client.cpp b/core/client.cpp
index 227ebaa..4147d34 100644
--- a/core/client.cpp
+++ b/core/client.cpp
@@ -9,9 +9,7 @@
#include <threads.h>
#include <algorithm>
-#include <array>
#include <map>
-#include <tuple>
#include <vector>
#include "logger.c"
@@ -54,16 +52,35 @@ enum Status {
STATUS_STOPPING,
};
-struct CompString {
- bool operator()(const char *a, const char *b) const {
- return strcmp(a, b) < 0;
- }
+struct Trace {
+ const char *name;
+ const char *name_fmt;
+ std::vector<ImS64> data;
+ Trace(const char *_name, const char *_name_fmt);
};
struct Plot {
const char *name;
const char *section;
std::vector<const char *> traces;
+ Plot(const char *_name, const char *_section, std::vector<const char *>_traces);
+ virtual void reset();
+ virtual void render();
+};
+
+struct PlotStacked : public Plot {
+ std::vector<std::vector<ImS64>> stacks;
+ std::vector<std::vector<double>> normals;
+ std::vector<ImS64> totals;
+ PlotStacked(const char *_name, const char *_section, std::vector<const char *>_traces);
+ void reset();
+ void push_stack(ImS64 index);
+ void push_normal(ImS64 index);
+ void render();
+};
+
+struct CompString {
+ bool operator()(const char *a, const char *b) const;
};
// ----------------------------------------------------------------------------
@@ -71,34 +88,26 @@ struct Plot {
// ----------------------------------------------------------------------------
#include "arch_plots.cpp"
-template <class T, size_t N, size_t M>
-std::array<T, N + M> merge_array(const std::array<T, N> &first, const std::array<T, M> &second) {
- std::array<T, N + M> result{};
- for (size_t i = 0; i < N; i++) result[i] = first[i];
- for (size_t i = 0; i < M; i++) result[N + i] = second[i];
- return result;
-}
-
-std::array g_core_traces = std::to_array<const char *>({
- "rowid",
- "step",
+std::vector<Trace> g_core_traces = {
+ {"rowid", nullptr},
+ {"step", nullptr},
#define FOR_CORE(i) \
- "cycl_" #i, \
- "mall_" #i, \
- "pnum_" #i, \
- "pfst_" #i, \
- "plst_" #i, \
- "amb0_" #i, \
- "amb1_" #i, \
- "emb0_" #i, \
- "emb1_" #i, \
- "eliv_" #i, \
- "edea_" #i,
+ {"cycl_" #i, "cycl_" #i}, \
+ {"mall_" #i, "mall_" #i}, \
+ {"pnum_" #i, "pnum_" #i}, \
+ {"pfst_" #i, "pfst_" #i}, \
+ {"plst_" #i, "plst_" #i}, \
+ {"amb0_" #i, "amb0_" #i}, \
+ {"amb1_" #i, "amb1_" #i}, \
+ {"emb0_" #i, "emb0_" #i}, \
+ {"emb1_" #i, "emb1_" #i}, \
+ {"eliv_" #i, "eliv_" #i}, \
+ {"edea_" #i, "edea_" #i},
FOR_CORES
#undef FOR_CORE
-});
+};
-std::array g_core_plots = std::to_array<Plot>({
+std::vector<Plot> g_core_plots = {
{"cycl", "general", {
#define FOR_CORE(i) "cycl_" #i,
FOR_CORES
@@ -129,14 +138,7 @@ std::array g_core_plots = std::to_array<Plot>({
FOR_CORES
#undef FOR_CORE
}},
-});
-
-std::map<const char *, std::vector<ImS64>, CompString> g_trace_map;
-std::array g_traces = merge_array(g_core_traces, g_arch_traces);
-std::array g_plots = merge_array(g_core_plots, g_arch_plots);
-
-#define TRACE_COUNT g_traces.size()
-#define PLOT_COUNT g_plots.size()
+};
// ----------------------------------------------------------------------------
// Globals
@@ -146,6 +148,7 @@ ImGuiIO *g_imgui_io;
ImGuiStyle *g_imgui_style;
ImPlotStyle *g_implot_style;
+// Data
const char *g_x_axes[] = {
"rowid",
"step",
@@ -162,29 +165,37 @@ int64_t g_x_low = DEFVAL_X_LOW;
int64_t g_x_high = DEFVAL_X_HIGH;
int64_t g_hm_left = DEFVAL_HM_LEFT;
int64_t g_hm_pixel_count = DEFVAL_HM_PIXEL_COUNT;
-int64_t g_hm_pixel_pow; // calculate on startup
+int64_t g_hm_pixel_pow; // calculate on init
int64_t g_x_current = -1l;
-thrd_t g_fetching_thread;
-mtx_t g_fetching_mutex;
int g_trace_len;
int g_trace_offset;
-// Layout globals
+thrd_t g_fetching_thread;
+mtx_t g_fetching_mutex;
+
+// Layout
bool g_data_col_visible = true;
bool g_plot_maximized;
bool g_plot_scroll;
float g_plot_scroll_current;
float g_plot_scroll_to;
float g_data_col_width;
-float g_plot_cells_top[PLOT_MAX_COLS][PLOT_COUNT];
-float g_plot_cells_bottom[PLOT_MAX_COLS][PLOT_COUNT];
-Plot *g_plot_cells[PLOT_MAX_COLS][PLOT_COUNT];
-Plot *g_plot_selected = &g_plots[0];
+std::vector<Plot *> g_plot_cells;
+std::vector<float> g_plot_cells_top;
+std::vector<float> g_plot_cells_bottom;
+std::vector<bool> g_plots_covered;
+Plot *g_plot_selected;
int g_plot_cols = 2;
int g_plot_col_selected;
int g_plot_row_selected;
+// Plots
+std::map<const char *, Trace *, CompString> g_trace_map;
+std::vector<Trace *> g_traces;
+std::vector<Plot *> g_plots;
+std::vector<double> g_x_axis_normal;
+
// ----------------------------------------------------------------------------
// Data functions
// ----------------------------------------------------------------------------
@@ -197,17 +208,23 @@ void data_on_field_change(void) {
g_nth = std::clamp(g_nth, DEFVAL_NTH, INT64_MAX);
g_x_low = std::clamp(g_x_low, DEFVAL_X_LOW, INT64_MAX);
g_x_high = std::clamp(g_x_high, g_x_low + 1l, DEFVAL_X_HIGH);
- #if !defined(MVEC_LOOP)
+#if !defined(MVEC_LOOP)
g_hm_left = std::clamp(g_hm_left, DEFVAL_HM_LEFT, (int64_t)MVEC_SIZE);
- #endif
+#endif
g_hm_pixel_count = std::clamp(g_hm_pixel_count, 1l, DEFVAL_HM_PIXEL_COUNT);
g_hm_pixel_pow = std::clamp(g_hm_pixel_pow, 0l, data_max_hm_pixel_pow());
g_x_current = -1l;
- for (auto &trace : g_trace_map) {
- trace.second.clear();
+ for (auto &trace : g_traces) {
+ trace->data.clear();
+ }
+
+ for (auto &plot : g_plots) {
+ plot->reset();
}
+
+ g_x_axis_normal.clear();
}
void data_reset_fields(void) {
@@ -227,13 +244,13 @@ void data_reset_fields(void) {
}
void data_reset_plot_cells(void) {
- memset(g_plot_cells, 0, PLOT_MAX_COLS * PLOT_COUNT * sizeof(nullptr));
- memset(g_plot_cells_top, 0, PLOT_MAX_COLS * PLOT_COUNT * sizeof(float));
- memset(g_plot_cells_bottom, 0, PLOT_MAX_COLS * PLOT_COUNT * sizeof(float));
+ std::fill(g_plot_cells.begin(), g_plot_cells.end(), nullptr);
+ std::fill(g_plot_cells_top.begin(), g_plot_cells_top.end(), 0.f);
+ std::fill(g_plot_cells_bottom.begin(), g_plot_cells_bottom.end(), 0.f);
}
void data_fetch(void) {
- struct json_object *request = json_object_new_object();
+ json_object *request = json_object_new_object();
json_object_object_add(request, "request", json_object_new_string("data"));
json_object_object_add(request, "entries", json_object_new_int64(g_entries));
json_object_object_add(request, "nth", json_object_new_int64(g_nth));
@@ -248,16 +265,16 @@ void data_fetch(void) {
log_info("Sending request to server: %s", request_str);
int socket_fd = socket(AF_INET, SOCK_STREAM, 0);
- struct sockaddr_in socket_addr;
- memset(&socket_addr, 0, sizeof(struct sockaddr_in));
+ sockaddr_in socket_addr;
+ memset(&socket_addr, 0, sizeof(sockaddr_in));
socket_addr.sin_family = AF_INET;
socket_addr.sin_port = htons(PORT);
inet_pton(AF_INET, IP, &socket_addr.sin_addr);
- if (connect(socket_fd, (struct sockaddr *)&socket_addr, sizeof(struct sockaddr_in))) assert(false);
+ if (connect(socket_fd, (sockaddr *)&socket_addr, sizeof(sockaddr_in))) assert(false);
json_object_to_fd(socket_fd, request, 0);
shutdown(socket_fd, SHUT_WR);
- struct json_object *response = json_object_from_fd(socket_fd);
+ json_object *response = json_object_from_fd(socket_fd);
const char *response_str = json_object_to_json_string(response);
log_info("Server responded with: %s", response_str);
@@ -267,23 +284,27 @@ void data_fetch(void) {
if (g_trace_map.contains(key)) {
for (size_t i = 0; i < json_object_array_length(value); i++) {
ImS64 point = json_object_get_int64(json_object_array_get_idx(value, i));
- g_trace_map.at(key).push_back(point);
+ g_trace_map[key]->data.push_back(point);
}
}
}
- g_x_current = g_trace_map.at(g_x_axes[g_x_axis]).back();
+ for (auto &val : g_trace_map[g_x_axes[g_x_axis]]->data) {
+ g_x_axis_normal.push_back((double)val);
+ }
+
+ g_x_current = g_trace_map[g_x_axes[g_x_axis]]->data.back();
json_object_put(request);
json_object_put(response);
- g_trace_len = g_trace_map.at("rowid").size();
+ g_trace_len = g_traces[0]->data.size();
if (g_trace_len >= g_entries * 2) {
- for (auto &trace : g_trace_map) {
- trace.second.erase(trace.second.begin(), trace.second.end() - g_entries);
+ for (auto &[key, trace] : g_trace_map) {
+ trace->data.erase(trace->data.begin(), trace->data.end() - g_entries);
}
- g_trace_len = g_trace_map.at("rowid").size();
+ g_trace_len = g_traces[0]->data.size();
}
g_trace_offset = 0;
@@ -428,7 +449,17 @@ void gui_render_data_col(void) {
ImGui::End();
}
-void gui_render_plot(const Plot *plot, const ImVec2 &frame_size=ImVec2(-1.f, 0.f)) {
+int gui_plot_cell_index(int col, int row) {
+ int index = col * PLOT_MAX_COLS + row;
+ assert(index < (int)g_plot_cells.size());
+ assert(index < (int)g_plot_cells_top.size());
+ assert(index < (int)g_plot_cells_bottom.size());
+ return index;
+}
+
+void gui_render_plot(Plot *plot, const ImVec2 &frame_size=ImVec2(-1.f, 0.f)) {
+ assert(plot);
+
int plot_flags = ImPlotFlags_NoMenus;
int axis_flags = ImPlotAxisFlags_NoMenus;
@@ -440,21 +471,17 @@ void gui_render_plot(const Plot *plot, const ImVec2 &frame_size=ImVec2(-1.f, 0.f
if (ImPlot::BeginPlot(plot->name, frame_size, plot_flags)) {
ImPlot::SetupAxes(nullptr, nullptr, axis_flags, axis_flags);
- ImS64 *x = g_trace_map.at(g_x_axes[g_x_axis]).data() + g_trace_offset;
-
- for (auto &trace : plot->traces) {
- ImS64 *y = g_trace_map.at(trace).data() + g_trace_offset;
- ImPlot::PlotLine(trace, x, y, g_trace_len);
- }
-
+ plot->render();
ImPlot::EndPlot();
}
}
void gui_render_plots(void) {
- const char *section_current = g_plots[0].section;
+ const char *section_current = g_plots[0]->section;
const char *section_next = nullptr;
- bool plots_covered[PLOT_COUNT] = { 0 };
+
+ g_plots_covered.clear();
+ g_plots_covered.resize(g_plots.size(), false);
const ImGuiViewport *viewport = ImGui::GetMainViewport();
const ImVec2 win_pos = g_data_col_visible ? ImVec2(g_data_col_width, viewport->Pos.y) : viewport->Pos;
@@ -480,32 +507,32 @@ void gui_render_plots(void) {
ImGui::SeparatorText(section_current);
ImGui::BeginTable("plots-table", g_plot_cols);
- for (size_t i = 0; i < PLOT_COUNT; i++) {
- if (g_plots[i].section != section_current) {
- section_next = (!section_next && !plots_covered[i]) ? g_plots[i].section : section_next;
+ for (size_t i = 0; i < g_plots.size(); i++) {
+ if (g_plots[i]->section != section_current) {
+ section_next = (!section_next && !g_plots_covered[i]) ? g_plots[i]->section : section_next;
continue;
}
ImGui::TableNextColumn();
- g_plot_cells[col][row] = &g_plots[i];
- g_plot_cells_top[col][row] = ImGui::GetCursorPosY();
+ g_plot_cells[gui_plot_cell_index(col, row)] = g_plots[i];
+ g_plot_cells_top[gui_plot_cell_index(col, row)] = ImGui::GetCursorPosY();
- if (&g_plots[i] == g_plot_selected) {
+ if (g_plots[i] == g_plot_selected) {
g_plot_col_selected = col;
g_plot_row_selected = row;
g_implot_style->Colors[ImPlotCol_FrameBg] = g_imgui_style->Colors[ImGuiCol_FrameBg];
}
- gui_render_plot(&g_plots[i]);
+ gui_render_plot(g_plots[i]);
- if (&g_plots[i] == g_plot_selected) {
+ if (g_plots[i] == g_plot_selected) {
g_implot_style->Colors[ImPlotCol_FrameBg] = COLOR_BLACK;
}
- g_plot_cells_bottom[col][row] = ImGui::GetCursorPosY();
+ g_plot_cells_bottom[gui_plot_cell_index(col, row)] = ImGui::GetCursorPosY();
col = (col + 1) % g_plot_cols;
row += col ? 0 : 1;
- plots_covered[i] = true;
+ g_plots_covered[i] = true;
}
section_current = section_next;
@@ -533,8 +560,8 @@ void gui_render_plot_maximized(void) {
void gui_plot_queue_scroll(int col_selected, int row_selected) {
const ImGuiViewport *viewport = ImGui::GetMainViewport();
- float plot_top = g_plot_cells_top[col_selected][row_selected];
- float plot_bottom = g_plot_cells_bottom[col_selected][row_selected];
+ float plot_top = g_plot_cells_top[gui_plot_cell_index(col_selected, row_selected)];
+ float plot_bottom = g_plot_cells_bottom[gui_plot_cell_index(col_selected, row_selected)];
float win_top = g_plot_scroll_current;
float win_bottom = win_top + viewport->Size.y;
@@ -565,7 +592,7 @@ void gui_render(void) {
// ----------------------------------------------------------------------------
// Main functions
// ----------------------------------------------------------------------------
-void win_sig_handler(int signo) {
+void app_sig_handler(int signo) {
(void)signo;
log_warn("Signal received, will stop SALIS data client...");
@@ -577,11 +604,11 @@ void win_sig_handler(int signo) {
glfwSetWindowShouldClose(g_window, GLFW_TRUE);
}
-void win_error_callback(int error, const char* description) {
+void app_error_callback(int error, const char* description) {
log_warn("GLFW error %d: %s", error, description);
}
-void win_key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) {
+void app_key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) {
(void)window;
(void)scancode;
@@ -626,20 +653,20 @@ void win_key_callback(GLFWwindow* window, int key, int scancode, int action, int
switch (key) {
case GLFW_KEY_LEFT:
g_plot_col_selected -= g_plot_col_selected ? 1 : 0;
- g_plot_selected = g_plot_cells[g_plot_col_selected][g_plot_row_selected];
+ g_plot_selected = g_plot_cells[gui_plot_cell_index(g_plot_col_selected, g_plot_row_selected)];
break;
case GLFW_KEY_RIGHT:
- g_plot_col_selected += (g_plot_col_selected < PLOT_MAX_COLS - 1 && g_plot_cells[g_plot_col_selected + 1][g_plot_row_selected]) ? 1 : 0;
- g_plot_selected = g_plot_cells[g_plot_col_selected][g_plot_row_selected];
+ g_plot_col_selected += (g_plot_col_selected < PLOT_MAX_COLS - 1 && g_plot_cells[gui_plot_cell_index(g_plot_col_selected + 1, g_plot_row_selected)]) ? 1 : 0;
+ g_plot_selected = g_plot_cells[gui_plot_cell_index(g_plot_col_selected, g_plot_row_selected)];
break;
case GLFW_KEY_UP:
- g_plot_row_selected -= g_plot_row_selected ? 1 : 0;
- g_plot_selected = g_plot_cells[g_plot_col_selected][g_plot_row_selected];
+ g_plot_row_selected -= (g_plot_row_selected && g_plot_cells[gui_plot_cell_index(g_plot_col_selected, g_plot_row_selected - 1)]) ? 1 : 0;
+ g_plot_selected = g_plot_cells[gui_plot_cell_index(g_plot_col_selected, g_plot_row_selected)];
gui_plot_queue_scroll(g_plot_col_selected, g_plot_row_selected);
break;
case GLFW_KEY_DOWN:
- g_plot_row_selected += (g_plot_row_selected < (int)PLOT_COUNT - 1 && g_plot_cells[g_plot_col_selected][g_plot_row_selected + 1]) ? 1 : 0;
- g_plot_selected = g_plot_cells[g_plot_col_selected][g_plot_row_selected];
+ g_plot_row_selected += (g_plot_row_selected < (int)g_plots.size() - 1 && g_plot_cells[gui_plot_cell_index(g_plot_col_selected, g_plot_row_selected + 1)]) ? 1 : 0;
+ g_plot_selected = g_plot_cells[gui_plot_cell_index(g_plot_col_selected, g_plot_row_selected)];
gui_plot_queue_scroll(g_plot_col_selected, g_plot_row_selected);
break;
case GLFW_KEY_F:
@@ -662,24 +689,21 @@ void win_key_callback(GLFWwindow* window, int key, int scancode, int action, int
}
}
-int main(int argc, char **argv) {
- (void)argc;
- (void)argv;
-
- signal(SIGINT, win_sig_handler);
- signal(SIGTERM, win_sig_handler);
+void init() {
+ signal(SIGINT, app_sig_handler);
+ signal(SIGTERM, app_sig_handler);
log_info("Starting SALIS data client");
log_info("Initializing GLFW");
- glfwSetErrorCallback(win_error_callback);
+ glfwSetErrorCallback(app_error_callback);
glfwInitHint(GLFW_WAYLAND_LIBDECOR, GLFW_WAYLAND_DISABLE_LIBDECOR);
if (!glfwInit()) assert(false);
float scale = ImGui_ImplGlfw_GetContentScaleForMonitor(glfwGetPrimaryMonitor());
g_window = glfwCreateWindow((int)(800 * scale), (int)(600 * scale), "SALIS data client", nullptr, nullptr);
assert(g_window);
- glfwSetKeyCallback(g_window, win_key_callback);
+ glfwSetKeyCallback(g_window, app_key_callback);
glfwMakeContextCurrent(g_window);
glfwSwapInterval(1); // enable vsync
@@ -705,15 +729,24 @@ int main(int argc, char **argv) {
ImGui_ImplGlfw_InitForOpenGL(g_window, true);
ImGui_ImplOpenGL3_Init(GLSL_VERSION);
- for (auto &i : g_traces) {
- g_trace_map[i] = {};
- g_trace_map[i].reserve(g_entries);
- }
+ for (auto &trace : g_core_traces) g_traces.push_back(&trace);
+ for (auto &trace : g_arch_traces) g_traces.push_back(&trace);
+ for (auto &plot : g_core_plots) g_plots.push_back(&plot);
+ for (auto &plot : g_arch_plots) g_plots.push_back(&plot);
+ for (auto &plot : g_arch_plots_stacked) g_plots.push_back(&plot);
+ for (auto &i : g_traces) g_trace_map[i->name] = i;
+
+ g_plot_cells = std::vector<Plot *>(g_plots.size() * PLOT_MAX_COLS);
+ g_plot_cells_top = std::vector<float>(g_plots.size() * PLOT_MAX_COLS);
+ g_plot_cells_bottom = std::vector<float>(g_plots.size() * PLOT_MAX_COLS);
+ g_plot_selected = g_plots[0];
g_hm_pixel_pow = data_max_hm_pixel_pow();
mtx_init(&g_fetching_mutex, mtx_plain);
+}
+void exec() {
while (!glfwWindowShouldClose(g_window)) {
glfwPollEvents();
@@ -733,7 +766,9 @@ int main(int argc, char **argv) {
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
glfwSwapBuffers(g_window);
}
+}
+void quit() {
mtx_destroy(&g_fetching_mutex);
ImGui_ImplOpenGL3_Shutdown();
@@ -744,6 +779,121 @@ int main(int argc, char **argv) {
log_info("Stopping SALIS data client");
glfwDestroyWindow(g_window);
glfwTerminate();
+}
+
+int main(int argc, char **argv) {
+ (void)argc;
+ (void)argv;
+
+ init();
+ exec();
+ quit();
return 0;
}
+
+// ----------------------------------------------------------------------------
+// Member definitions
+// ----------------------------------------------------------------------------
+Trace::Trace(const char *_name, const char *_name_fmt) {
+ name = _name;
+ name_fmt = _name_fmt;
+}
+
+// Regular line plot
+Plot::Plot(const char *_name, const char *_section, std::vector<const char *>_traces) {
+ name = _name;
+ section = _section;
+ traces = _traces;
+}
+
+void Plot::reset() {}
+
+void Plot::render() {
+ ImS64 *x = g_trace_map[g_x_axes[g_x_axis]]->data.data() + g_trace_offset;
+
+ for (auto &trace : traces) {
+ Trace *trace_obj = g_trace_map[trace];
+ ImS64 *y = trace_obj->data.data() + g_trace_offset;
+ ImPlot::PlotLine(trace_obj->name_fmt, x, y, g_trace_len);
+ }
+}
+
+// Stacked percentage plot
+PlotStacked::PlotStacked(const char *_name, const char *_section, std::vector<const char *>_traces) : Plot(_name, _section, _traces) {
+ stacks.resize(_traces.size());
+ normals.resize(_traces.size());
+}
+
+void PlotStacked::reset() {
+ for (auto &stack : stacks) stack.clear();
+ for (auto &normal : normals) normal.clear();
+ totals.clear();
+}
+
+void PlotStacked::push_stack(ImS64 index) {
+ ImS64 new_total = 0;
+
+ for (int i = 0; i < (int)traces.size(); i++) {
+ ImS64 trace_val = g_trace_map[traces[i]]->data[index];
+ stacks[i].push_back(trace_val + new_total);
+ new_total += trace_val;
+ }
+
+ totals.push_back(new_total);
+}
+
+void PlotStacked::push_normal(ImS64 index) {
+ if (totals[index]) {
+ for (int i = 0; i < (int)traces.size(); i++) {
+ double stack_val = (double)stacks[i][index];
+ double total_val = (double)totals[index];
+ normals[i].push_back(stack_val / total_val);
+ }
+ } else {
+ for (int i = 0; i < (int)traces.size(); i++) {
+ normals[i].push_back(0.);
+ }
+ }
+}
+
+void PlotStacked::render() {
+ int totals_size = totals.size();
+
+ if (totals_size < g_trace_len) {
+ for (int i = totals_size; i < g_trace_len; i++) {
+ push_stack(i);
+ }
+
+ for (int i = totals_size; i < g_trace_len; i++) {
+ push_normal(i);
+ }
+ }
+
+ Trace *trace_0 = g_trace_map[traces[0]];
+ double *x = g_x_axis_normal.data() + g_trace_offset;
+ double *y_0 = normals[0].data() + g_trace_offset;
+
+ ImPlot::PlotShaded(trace_0->name_fmt, x, y_0, g_trace_len, 0);
+
+ for (int i = 1; i < (int)traces.size(); i++) {
+ Trace *trace_i = g_trace_map[traces[i]];
+ double *y_im1 = normals[i - 1].data() + g_trace_offset;
+ double *y_i = normals[i].data() + g_trace_offset;
+ ImPlot::PlotShaded(trace_i->name_fmt, x, y_im1, y_i, g_trace_len);
+ }
+
+ ImPlot::PlotLine(trace_0->name_fmt, x, y_0, g_trace_len);
+
+ for (int i = 1; i < (int)traces.size(); i++) {
+ Trace *trace_i = g_trace_map[traces[i]];
+ double *y_i = normals[i].data() + g_trace_offset;
+ double *y_im1 = normals[i - 1].data() + g_trace_offset;
+ ImPlot::PlotLine(trace_i->name_fmt, x, y_im1, g_trace_len);
+ ImPlot::PlotLine(trace_i->name_fmt, x, y_i, g_trace_len);
+ }
+}
+
+bool CompString::operator()(const char *a, const char *b) const {
+ return strcmp(a, b) < 0;
+}