"Fossies" - the Fresh Open Source Software Archive

Member "tcpflow-1.6.1/src/netviz/plot_view.cpp" (19 Feb 2021, 13103 Bytes) of package /linux/misc/tcpflow-1.6.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "plot_view.cpp" see the Fossies "Dox" file reference documentation.

    1 /**
    2  * plot_view.cpp: 
    3  * Render titles, axes, and legends for various plots
    4  *
    5  * This source file is public domain, as it is not based on the original tcpflow.
    6  *
    7  * Author: Michael Shick <mike@shick.in>
    8  *
    9  */
   10 
   11 #include "config.h"
   12 #include "tcpflow.h"                    // for ssprintf
   13 #include "plot_view.h"
   14 
   15 #ifdef HAVE_LIBCAIRO
   16 #include <math.h>
   17 
   18 const double plot_view::rgb_t::epsilon = 1.0 / 256.0;
   19 const double plot_view::text_line_base_width = 0.05;
   20 const double plot_view::span_arrow_angle = M_PI / 4.0;
   21 const double plot_view::span_stop_angle = M_PI / 2.0;
   22 const std::vector<std::string> plot_view::size_suffixes =
   23         plot_view::build_size_suffixes();
   24 
   25 void plot_view::render(cairo_t *cr, const plot_view::bounds_t &bounds) {
   26     cairo_matrix_t original_matrix;
   27     cairo_get_matrix(cr, &original_matrix);
   28 
   29     // purple background for padding checking
   30     //cairo_set_source_rgb(cr, 0.50, 0.00, 0.50);
   31     //cairo_rectangle(cr, bounds.x, bounds.y, bounds.width, bounds.height);
   32     //cairo_fill(cr);
   33 
   34     double pad_left = width * pad_left_factor;
   35     double pad_top = height * pad_top_factor;
   36     double pad_bottom = height * pad_bottom_factor;
   37     double pad_right = width * pad_right_factor;
   38 
   39     // compute bounds for subclasses to render content into
   40     bounds_t content_bounds;
   41 
   42     content_bounds.x = bounds.x + pad_left;
   43     content_bounds.y = bounds.y + pad_top;
   44     content_bounds.width = bounds.width - pad_right - pad_left;
   45     content_bounds.height = bounds.height - pad_bottom - pad_top;
   46 
   47     cairo_text_extents_t title_extents;
   48     cairo_text_extents_t subtitle_extents;
   49     double font_size_title = title_font_size;
   50 
   51     cairo_translate(cr, bounds.x, bounds.y);
   52 
   53     double title_base_y = 0.0;
   54     if(title_on_bottom) {
   55         title_base_y = bounds.height - pad_bottom;
   56     }
   57 
   58     cairo_select_font_face(cr, "Sans",
   59                CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
   60     cairo_set_font_size(cr, font_size_title);
   61     cairo_set_source_rgb(cr, 0, 0, 0);
   62     cairo_text_extents(cr, title.c_str(), &title_extents);
   63     // Is the title too wide?
   64     double title_max_width = bounds.width * title_max_width_ratio;
   65     if(title_extents.width > title_max_width) {
   66         // scale the font size accordingly
   67         font_size_title *= title_max_width / title_extents.width;
   68         cairo_set_font_size(cr, font_size_title);
   69         cairo_text_extents(cr, title.c_str(), &title_extents);
   70     }
   71     // derive subtitle size and measure
   72     double font_size_subtitle = font_size_title *
   73         subtitle_font_size_factor;
   74     cairo_set_font_size(cr, font_size_subtitle);
   75     cairo_text_extents(cr, subtitle.c_str(), &subtitle_extents);
   76     double intertitle_padding = subtitle_extents.height *
   77         subtitle_y_pad_factor;
   78     cairo_set_font_size(cr, font_size_title);
   79     double title_padded_height = title_extents.height *
   80         title_y_pad_factor;
   81     // render title text
   82     cairo_move_to(cr, (bounds.width - title_extents.width) / 2.0,
   83           title_base_y + title_extents.height +
   84           (title_padded_height - title_extents.height) / 2);
   85     cairo_show_text(cr, title.c_str());
   86     // render subtitle text
   87     cairo_set_font_size(cr, font_size_subtitle);
   88     cairo_move_to(cr, (bounds.width - subtitle_extents.width) / 2.0,
   89           title_base_y + ((title_padded_height - title_extents.height) / 2) +
   90           title_extents.height + intertitle_padding +
   91           subtitle_extents.height);
   92     cairo_show_text(cr, subtitle.c_str());
   93 
   94     // render axis labels
   95 
   96     cairo_matrix_t unrotated_matrix;
   97     cairo_get_matrix(cr, &unrotated_matrix);
   98     cairo_text_extents_t axis_label_extents;
   99     cairo_set_font_size(cr, y_axis_font_size);
  100     cairo_text_extents(cr, y_label.c_str(), &axis_label_extents);
  101     double y_label_x = 0.0 + axis_label_extents.height;
  102     double y_label_centering_pad = ((content_bounds.height - axis_label_extents.width) / 2.0);
  103     double y_label_y = pad_top + y_label_centering_pad + axis_label_extents.width;
  104     cairo_move_to(cr, y_label_x, y_label_y);
  105     cairo_rotate(cr, -M_PI / 2.0);
  106     cairo_show_text(cr, y_label.c_str());
  107     cairo_set_matrix(cr, &unrotated_matrix);
  108     // add y axis decoration
  109     // TODO not implemented for brevity
  110 
  111     cairo_set_font_size(cr, x_axis_font_size);
  112     cairo_text_extents(cr, x_label.c_str(), &axis_label_extents);
  113     double x_label_centering_pad = (content_bounds.width - axis_label_extents.width) / 2.0;
  114     double x_label_x = pad_left + x_label_centering_pad;
  115     double x_label_y = bounds.height;
  116     cairo_move_to(cr, x_label_x, x_label_y);
  117     cairo_show_text(cr, x_label.c_str());
  118 
  119     // add x axis decoration
  120     if(x_axis_decoration == AXIS_SPAN_ARROW || x_axis_decoration == AXIS_SPAN_STOP) {
  121         double angle = span_arrow_angle;
  122         double line_width = x_axis_font_size * text_line_base_width;
  123         double tip_length = line_width * 10.0;
  124         if(x_axis_decoration == AXIS_SPAN_STOP) {
  125             angle = span_stop_angle;
  126             tip_length = line_width * 5.0;
  127         }
  128         double gap = line_width * 10.0;
  129         double x = x_label_x - gap;
  130         double y = x_label_y - axis_label_extents.height / 3.0;
  131         double pr_x, pr_y; // previous x and y positions
  132         // left of label
  133         cairo_move_to(cr, x, y);
  134         pr_x = x;
  135         pr_y = y;
  136         x = pr_x - (x_label_centering_pad - gap);
  137         y = pr_y;
  138         cairo_line_to(cr, x, y);
  139         pr_x = x;
  140         pr_y = y;
  141         x = pr_x + tip_length * sin(angle + M_PI / 2.0);
  142         y = pr_y + tip_length * cos(angle + M_PI / 2.0);
  143         cairo_line_to(cr, x, y);
  144         cairo_move_to(cr, pr_x, pr_y);
  145         x = pr_x + tip_length * sin(-angle + M_PI / 2.0);
  146         y = pr_y + tip_length * cos(-angle + M_PI / 2.0);
  147         cairo_line_to(cr, x, y);
  148         // right of label
  149         x = x_label_x + axis_label_extents.width + gap;
  150         y = x_label_y - axis_label_extents.height / 3.0;
  151         cairo_move_to(cr, x, y);
  152         pr_x = x;
  153         pr_y = y;
  154         x = pr_x + (x_label_centering_pad - gap);
  155         y = pr_y;
  156         cairo_line_to(cr, x, y);
  157         pr_x = x;
  158         pr_y = y;
  159         x = pr_x + tip_length * sin(angle - M_PI / 2.0);
  160         y = pr_y - tip_length * cos(angle - M_PI / 2.0);
  161         cairo_line_to(cr, x, y);
  162         cairo_move_to(cr, pr_x, pr_y);
  163         x = pr_x + tip_length * sin(-angle - M_PI / 2.0);
  164         y = pr_y - tip_length * cos(-angle - M_PI / 2.0);
  165         cairo_line_to(cr, x, y);
  166         cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
  167         cairo_set_line_width(cr, line_width);
  168         cairo_stroke(cr);
  169     }
  170 
  171     // render ticks
  172 
  173     double tick_length = bounds.width * tick_length_factor;
  174     double tick_width = bounds.height * tick_width_factor;
  175 
  176     // y ticks (packet counts)
  177 
  178     cairo_set_font_size(cr, y_tick_font_size);
  179 
  180     // translate down so the top of the window aligns with the top of
  181     // the graph itself
  182     cairo_translate(cr, 0, pad_top);
  183 
  184     double y_height = bounds.height - pad_bottom - pad_top;
  185     double y_tick_spacing = 0.0;
  186     if(y_tick_labels.size() > 1) {
  187         y_tick_spacing = y_height / (double) (y_tick_labels.size() - 1);
  188     }
  189     for(size_t ii = 0; ii < y_tick_labels.size(); ii++) {
  190         cairo_text_extents_t label_extents;
  191         double yy = y_height - (((double) ii) * y_tick_spacing);
  192         std::string label = y_tick_labels.at(ii);
  193 
  194         cairo_text_extents(cr, label.c_str(),
  195                &label_extents);
  196         cairo_move_to(cr, (pad_left - tick_length - label_extents.width),
  197           yy + (label_extents.height / 2));
  198         cairo_show_text(cr, label.c_str());
  199 
  200         // tick mark
  201         cairo_rectangle(cr, pad_left - tick_length, yy - (tick_width / 2),
  202                 tick_length, tick_width);
  203         cairo_fill(cr);
  204     }
  205 
  206     // right ticks (packet counts)
  207 
  208     cairo_set_font_size(cr, right_tick_font_size);
  209 
  210     if(right_tick_labels.size() > 1) {
  211         y_tick_spacing = y_height / (double) (right_tick_labels.size() - 1);
  212     }
  213     for(size_t ii = 0; ii < right_tick_labels.size(); ii++) {
  214         cairo_text_extents_t label_extents;
  215         double yy = y_height - (((double) ii) * y_tick_spacing);
  216         std::string label = right_tick_labels.at(ii);
  217 
  218         cairo_text_extents(cr, label.c_str(),
  219                &label_extents);
  220         cairo_move_to(cr, (bounds.width - pad_right + tick_length),
  221           yy + (label_extents.height / 2));
  222         cairo_show_text(cr, label.c_str());
  223 
  224         // tick mark
  225         cairo_rectangle(cr, bounds.width - pad_right, yy - (tick_width / 2),
  226                 tick_length, tick_width);
  227         cairo_fill(cr);
  228     }
  229     cairo_set_matrix(cr, &original_matrix);
  230     cairo_translate(cr, bounds.x, bounds.y);
  231 
  232     // x ticks (time)
  233     // TODO prevent overlap
  234 
  235     cairo_set_font_size(cr, x_tick_font_size);
  236 
  237     cairo_translate(cr, pad_left, bounds.height - pad_bottom);
  238 
  239     double x_width = bounds.width - (pad_right + pad_left);
  240     double x_tick_spacing = x_width / (x_tick_labels.size() - 1);
  241 
  242     for(size_t ii = 0; ii < x_tick_labels.size(); ii++) {
  243         cairo_text_extents_t label_extents;
  244         double xx = ii * x_tick_spacing;
  245 
  246         const char *label = x_tick_labels.at(ii).c_str();
  247 
  248         cairo_text_extents(cr, label, &label_extents);
  249         double pad = ((label_extents.height * x_tick_label_pad_factor) -
  250                 label_extents.height) / 2;
  251 
  252         // prevent labels from running off the edge of the image
  253         double label_x = xx - (label_extents.width / 2.0);
  254         label_x = std::max(label_x, - pad_left);
  255         label_x = std::min(bounds.width - label_extents.width, label_x);
  256 
  257         cairo_move_to(cr, label_x, label_extents.height + pad);
  258         cairo_show_text(cr, label);
  259     }
  260 
  261     cairo_set_matrix(cr, &original_matrix);
  262     cairo_translate(cr, bounds.x, bounds.y);
  263 
  264     // render legend
  265 
  266     cairo_text_extents_t legend_label_extents;
  267     double chip_length = 0.0;
  268 
  269     // derive color chip size from largest label height
  270     for(size_t ii = 0; ii < legend.size(); ii++) {
  271         const legend_entry_t &entry = legend.at(ii);
  272         cairo_text_extents(cr, entry.label.c_str(), &legend_label_extents);
  273 
  274         chip_length = std::max(chip_length, legend_label_extents.height);
  275     }
  276     chip_length *= legend_chip_factor;
  277 
  278     cairo_translate(cr, bounds.width - (pad_right * 0.9),
  279         pad_top);
  280 
  281     cairo_set_font_size(cr, legend_font_size);
  282 
  283     for(size_t ii = 0; ii < legend.size(); ii++) {
  284         const legend_entry_t &entry = legend.at(ii);
  285 
  286         // chip
  287         cairo_set_source_rgb(cr, entry.color.r, entry.color.g,
  288              entry.color.b);
  289         cairo_rectangle(cr, 0, 0, chip_length, chip_length);
  290         cairo_fill(cr);
  291 
  292         // label
  293         cairo_set_source_rgb(cr, 0, 0, 0);
  294         cairo_text_extents(cr, entry.label.c_str(),
  295                &legend_label_extents);
  296         cairo_move_to(cr, chip_length * 1.2, (chip_length / 2.0) +
  297                 (legend_label_extents.height / 2.0));
  298         cairo_show_text(cr, entry.label.c_str());
  299 
  300         // translate down for the next legend entry
  301         cairo_translate(cr, 0, chip_length);
  302     }
  303 
  304     cairo_set_source_rgb(cr, 0, 0, 0);
  305     cairo_set_matrix(cr, &original_matrix);
  306 
  307     // render axes and update content bounds
  308     double axis_width = bounds.height * axis_thickness_factor;
  309 
  310     cairo_rectangle(cr, content_bounds.x, content_bounds.y, axis_width,
  311             content_bounds.height);
  312     cairo_rectangle(cr, content_bounds.x,
  313             content_bounds.y + (content_bounds.height - axis_width),
  314             content_bounds.width, axis_width);
  315     // if there are right hand ticks, draw a right-hand axis
  316     if(right_tick_labels.size() > 0) {
  317         cairo_rectangle(cr, content_bounds.x + content_bounds.width - axis_width, content_bounds.y, axis_width,
  318                 content_bounds.height);
  319     }
  320     cairo_fill(cr);
  321 
  322     content_bounds.x += axis_width;
  323     content_bounds.width -= axis_width;
  324     if(right_tick_labels.size() > 0) {
  325         content_bounds.width -= axis_width;
  326     }
  327     content_bounds.height -= axis_width;
  328 
  329     // render data!
  330 
  331     render_data(cr, content_bounds);
  332 }
  333 
  334 std::string plot_view::pretty_byte_total(uint64_t byte_count, uint8_t precision)
  335 {
  336     //// packet count/size
  337     uint64_t size_log_1000 = (uint64_t) (log(byte_count) / log(1000));
  338     if(size_log_1000 >= size_suffixes.size()) {
  339         size_log_1000 = 0;
  340     }
  341     // only put decimal places if using a unit less granular than the byte (2.00 bytes looks silly)
  342     if(size_log_1000 == 0) {
  343         precision = 0;
  344     }
  345     return ssprintf("%.*f %sB", precision, (double) byte_count / pow(1000.0, (double) size_log_1000),
  346             size_suffixes.at(size_log_1000).c_str());
  347 }
  348 
  349 std::string plot_view::pretty_byte_total(uint64_t byte_count)
  350 {
  351     return pretty_byte_total(byte_count, 2);
  352 }
  353 
  354 std::vector<std::string> plot_view::build_size_suffixes()
  355 {
  356     std::vector<std::string> v;
  357     v.push_back("");
  358     v.push_back("K");
  359     v.push_back("M");
  360     v.push_back("G");
  361     v.push_back("T");
  362     v.push_back("P");
  363     v.push_back("E");
  364     return v;
  365 }
  366 #endif