"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