Files
nvtop/src/plot.c
2024-12-09 12:57:02 +01:00

137 lines
5.1 KiB
C

/*
*
* Copyright (C) 2019-2021 Maxime Schmitt <maxime.schmitt91@gmail.com>
*
* This file is part of Nvtop.
*
* Nvtop is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Nvtop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with nvtop. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "nvtop/plot.h"
#include "nvtop/common.h"
#include <assert.h>
#include <ncurses.h>
#include <stdbool.h>
#include <string.h>
#include <tgmath.h>
static inline int data_level(double rows, double data, double increment) {
return (int)(rows - round(data / increment));
}
void nvtop_line_plot(WINDOW *win, size_t num_data, const double *data, unsigned num_lines, bool legend_left,
char legend[MAX_LINES_PER_PLOT][PLOT_MAX_LEGEND_SIZE]) {
if (num_data == 0)
return;
int rows, cols;
getmaxyx(win, rows, cols);
rows -= 1;
double increment = 100. / (double)(rows);
assert(num_lines <= MAX_LINES_PER_PLOT && "Cannot plot more than " EXPAND_AND_QUOTE(MAX_LINES_PER_PLOT) " lines");
unsigned lvl_before[MAX_LINES_PER_PLOT];
for (size_t k = 0; k < num_lines; ++k)
lvl_before[k] = data_level(rows, data[k], increment);
for (size_t i = 0; i < num_data || i < (size_t)cols; i += num_lines) {
for (unsigned k = 0; k < num_lines; ++k) {
unsigned lvl_now_k = data_level(rows, data[i + k], increment);
wcolor_set(win, k + 1, NULL);
// Three cases: has increased, has decreased and remained level
if (lvl_before[k] < lvl_now_k || lvl_before[k] > lvl_now_k) {
// Case 1 and 2: has increased/decreased
// An increase goes down on the plot because (0,0) is top left
bool drawing_down = lvl_before[k] < lvl_now_k;
unsigned bottom = drawing_down ? lvl_before[k] : lvl_now_k;
unsigned top = drawing_down ? lvl_now_k : lvl_before[k];
// Draw the vertical line corners
mvwaddch(win, bottom, i + k, drawing_down ? ACS_URCORNER : ACS_ULCORNER);
mvwaddch(win, top, i + k, drawing_down ? ACS_LLCORNER : ACS_LRCORNER);
// Draw the vertical line between the corners
if (top - bottom > 1) {
mvwvline(win, bottom + 1, i + k, 0, top - bottom - 1);
}
// Draw the continuation of the other metrics
for (unsigned j = 0; j < num_lines; ++j) {
if (j != k) {
if (lvl_before[j] == top)
// The continuation is at the same level as the bottom corner
mvwaddch(win, top, i + k, ACS_BTEE);
else if (lvl_before[j] == bottom)
// The continuation is at the same level as the top corner
mvwaddch(win, bottom, i + k, ACS_TTEE);
else if (lvl_before[j] > bottom && lvl_before[j] < top)
// The continuation lies on the vertical line
mvwaddch(win, lvl_before[j], i + k, ACS_PLUS);
else {
// The continuation lies outside the update interval so keep the
// color
wcolor_set(win, j + 1, NULL);
mvwaddch(win, lvl_before[j], i + k, ACS_HLINE);
wcolor_set(win, k + 1, NULL);
}
}
}
} else {
// Case 3: stayed level
mvwhline(win, lvl_now_k, i + k, 0, 1);
for (unsigned j = 0; j < num_lines; ++j) {
if (j != k) {
if (lvl_before[j] != lvl_now_k) {
// Add the continuation of other metric lines
wcolor_set(win, j + 1, NULL);
mvwaddch(win, lvl_before[j], i + k, ACS_HLINE);
wcolor_set(win, k + 1, NULL);
}
}
}
}
lvl_before[k] = lvl_now_k;
}
}
int plot_y_position = 0;
for (unsigned i = 0; i < num_lines && plot_y_position < rows; ++i) {
wcolor_set(win, i + 1, NULL);
if (legend_left) {
mvwprintw(win, plot_y_position, 0, "%.*s", cols, legend[i]);
} else {
size_t length = strlen(legend[i]);
if (length <= (size_t)cols) {
mvwprintw(win, plot_y_position, cols - length, "%s", legend[i]);
} else {
mvwprintw(win, plot_y_position, 0, "%.*s", (int)(length - cols), legend[i]);
}
}
plot_y_position++;
}
}
void draw_rectangle(WINDOW *win, unsigned startX, unsigned startY, unsigned sizeX, unsigned sizeY) {
mvwhline(win, startY, startX + 1, 0, sizeX - 2);
mvwhline(win, startY + sizeY - 1, startX + 1, 0, sizeX - 2);
mvwvline(win, startY + 1, startX, 0, sizeY - 2);
mvwvline(win, startY + 1, startX + sizeX - 1, 0, sizeY - 2);
mvwaddch(win, startY, startX, ACS_ULCORNER);
mvwaddch(win, startY, startX + sizeX - 1, ACS_URCORNER);
mvwaddch(win, startY + sizeY - 1, startX, ACS_LLCORNER);
mvwaddch(win, startY + sizeY - 1, startX + sizeX - 1, ACS_LRCORNER);
}