Skip to content
Snippets Groups Projects
Commit f2088850 authored by tag2y19's avatar tag2y19 Committed by Tom Greig
Browse files

Labels and titles and ticks!

Lots of pangocairo :)
parent a6bb02db
No related branches found
No related tags found
No related merge requests found
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
#include <ctype.h> #include <ctype.h>
#include <fcntl.h> #include <fcntl.h>
#include <gtk/gtk.h> #include <gtk/gtk.h>
#include <pango/pangocairo.h>
/* TYPES!!! */ /* TYPES!!! */
...@@ -45,6 +46,17 @@ struct context { ...@@ -45,6 +46,17 @@ struct context {
struct timespec start_time; struct timespec start_time;
GtkWidget* plot; GtkWidget* plot;
PangoFontDescription* font_description;
char* title;
PangoLayout* title_layout;
char* xlab;
PangoLayout* xlab_layout;
char* yllab;
PangoLayout* yllab_layout;
char* yrlab;
PangoLayout* yrlab_layout;
double margins[4];
char* xaxis; char* xaxis;
size_t xaxis_n; size_t xaxis_n;
...@@ -63,6 +75,11 @@ struct context { ...@@ -63,6 +75,11 @@ struct context {
double yrdist; /* 0 for fit all */ double yrdist; /* 0 for fit all */
double yrpos; double yrpos;
struct {
PangoLayout** layouts;
int n;
} xtick_layouts, yltick_layouts, yrtick_layouts;
}; };
struct fd_source { struct fd_source {
...@@ -102,6 +119,8 @@ int main( int argc, char** argv ) { ...@@ -102,6 +119,8 @@ int main( int argc, char** argv ) {
context->read_buffer_len = 512; context->read_buffer_len = 512;
context->xfit = true; context->xfit = true;
context->xfollow = true; context->xfollow = true;
context->font_description =
pango_font_description_from_string( "Sans 12px" );
static const char* colours[] = { static const char* colours[] = {
"#005C84", "#FCBC00", "#0C838C", "#E63037", "#005C84", "#FCBC00", "#0C838C", "#E63037",
...@@ -135,7 +154,8 @@ int main( int argc, char** argv ) { ...@@ -135,7 +154,8 @@ int main( int argc, char** argv ) {
.flags = G_OPTION_FLAG_IN_MAIN, .flags = G_OPTION_FLAG_IN_MAIN,
.arg = G_OPTION_ARG_CALLBACK, .arg = G_OPTION_ARG_CALLBACK,
.arg_data = &add_to_column_list, .arg_data = &add_to_column_list,
.description = "Use right axis for column", .description = "Use right axis for column "
"(can be given multiple times)",
.arg_description = "column" .arg_description = "column"
}, { }, {
.long_name = "ignore", .long_name = "ignore",
...@@ -143,8 +163,41 @@ int main( int argc, char** argv ) { ...@@ -143,8 +163,41 @@ int main( int argc, char** argv ) {
.flags = G_OPTION_FLAG_IN_MAIN, .flags = G_OPTION_FLAG_IN_MAIN,
.arg = G_OPTION_ARG_CALLBACK, .arg = G_OPTION_ARG_CALLBACK,
.arg_data = &add_to_column_list, .arg_data = &add_to_column_list,
.description = "Do not plot the data in column", .description = "Do not plot the data in column "
"(can be given multiple times)",
.arg_description = "column" .arg_description = "column"
}, {
.long_name = "title",
.short_name = 't',
.flags = G_OPTION_FLAG_IN_MAIN,
.arg = G_OPTION_ARG_STRING,
.arg_data = &( context->title ),
.description = "Main graph title",
.arg_description = "title",
}, {
.long_name = "xlab",
.short_name = '\0',
.flags = G_OPTION_FLAG_IN_MAIN,
.arg = G_OPTION_ARG_STRING,
.arg_data = &( context->xlab ),
.description = "X-Axis label",
.arg_description = "label",
}, {
.long_name = "ylab",
.short_name = '\0',
.flags = G_OPTION_FLAG_IN_MAIN,
.arg = G_OPTION_ARG_STRING,
.arg_data = &( context->yllab ),
.description = "Left y-axis label",
.arg_description = "label",
}, {
.long_name = "yrlab",
.short_name = '\0',
.flags = G_OPTION_FLAG_IN_MAIN,
.arg = G_OPTION_ARG_STRING,
.arg_data = &( context->yrlab ),
.description = "Right y-axis label",
.arg_description = "label",
}, { }, {
NULL, NULL,
}, },
...@@ -786,6 +839,66 @@ static void plot_draw( GtkDrawingArea* plot, ...@@ -786,6 +839,66 @@ static void plot_draw( GtkDrawingArea* plot,
struct context* context = (struct context*) data; struct context* context = (struct context*) data;
(void) plot; (void) plot;
/* Texts */
double margin_reqs[4] = { 5., 5., 5., 5. };
if ( NULL == context->title_layout && NULL != context->title ) {
context->title_layout = pango_cairo_create_layout( cr );
pango_layout_set_font_description( context->title_layout,
context->font_description );
pango_layout_set_text(
context->title_layout, context->title, -1 );
}
if ( NULL != context->title_layout ) {
int height;
pango_layout_get_pixel_size(
context->title_layout, NULL, &height );
margin_reqs[2] += height + 3;
}
if ( NULL == context->xlab_layout && NULL != context->xlab ) {
context->xlab_layout = pango_cairo_create_layout( cr );
pango_layout_set_font_description( context->xlab_layout,
context->font_description );
pango_layout_set_text(
context->xlab_layout, context->xlab, -1 );
}
if ( NULL != context->xlab_layout ) {
int height;
pango_layout_get_pixel_size(
context->xlab_layout, NULL, &height );
margin_reqs[0] += height + 3;
}
if ( NULL == context->yllab_layout && NULL != context->yllab ) {
context->yllab_layout = pango_cairo_create_layout( cr );
pango_layout_set_font_description( context->yllab_layout,
context->font_description );
pango_layout_set_text(
context->yllab_layout, context->yllab, -1 );
}
if ( NULL != context->yllab_layout ) {
int height;
pango_layout_get_pixel_size(
context->yllab_layout, NULL, &height );
margin_reqs[1] += height + 3;
}
if ( NULL == context->yrlab_layout && NULL != context->yrlab ) {
context->yrlab_layout = pango_cairo_create_layout( cr );
pango_layout_set_font_description( context->yrlab_layout,
context->font_description );
pango_layout_set_text(
context->yrlab_layout, context->yrlab, -1 );
}
if ( NULL != context->yrlab_layout ) {
int height;
pango_layout_get_pixel_size(
context->yrlab_layout, NULL, &height );
margin_reqs[3] += height + 3;
}
/* Clear */ /* Clear */
cairo_save( cr ); cairo_save( cr );
...@@ -793,19 +906,6 @@ static void plot_draw( GtkDrawingArea* plot, ...@@ -793,19 +906,6 @@ static void plot_draw( GtkDrawingArea* plot,
cairo_paint( cr ); cairo_paint( cr );
cairo_restore( cr ); cairo_restore( cr );
/* Axes */
cairo_save( cr );
cairo_set_source_rgb( cr, 0, 0, 0 );
cairo_set_line_width( cr, 1 );
cairo_move_to( cr, width - 5, height - 5 );
cairo_line_to( cr, 5, height - 5 );
cairo_line_to( cr, 5, 5 );
cairo_line_to( cr, width - 5, 5 );
cairo_line_to( cr, width - 5, height - 5 );
cairo_stroke( cr );
cairo_restore( cr );
if ( NULL == context->data ) { if ( NULL == context->data ) {
return; return;
} }
...@@ -919,8 +1019,128 @@ static void plot_draw( GtkDrawingArea* plot, ...@@ -919,8 +1019,128 @@ static void plot_draw( GtkDrawingArea* plot,
} }
int xdivs = pretty( &( xrange[0] ), &( xrange[1] ), 10 ); int xdivs = pretty( &( xrange[0] ), &( xrange[1] ), 10 );
int yldivs = pretty( &( ylrange[0] ), &( ylrange[1] ), 10 ); int yldivs = lset ?
int yrdivs = pretty( &( yrrange[0] ), &( yrrange[1] ), 10 ); pretty( &( ylrange[0] ), &( ylrange[1] ), 10 ) : 0;
int yrdivs = rset ?
pretty( &( yrrange[0] ), &( yrrange[1] ), 10 ) : 0;
char tick_buffer[512];
if ( xdivs > context->xtick_layouts.n ) {
context->xtick_layouts.layouts = realloc(
context->xtick_layouts.layouts,
xdivs * sizeof *( context->xtick_layouts.layouts ) );
for ( int i = context->xtick_layouts.n; i < xdivs; i++ ) {
context->xtick_layouts.layouts[i] = NULL;
}
context->xtick_layouts.n = xdivs;
}
int max_tick_size = 0;
for ( int i = 0; i < xdivs; i++ ) {
if ( NULL == context->xtick_layouts.layouts[i] ) {
context->xtick_layouts.layouts[i] =
pango_cairo_create_layout( cr );
pango_layout_set_font_description(
context->xtick_layouts.layouts[i],
context->font_description );
}
sprintf( tick_buffer, "%.3g",
xrange[0] + ( (double) i ) / ( xdivs - 1 ) * ( xrange[1] - xrange[0] ) );
pango_layout_set_text(
context->xtick_layouts.layouts[i], tick_buffer, -1 );
int tick_size;
pango_layout_get_pixel_size( context->xtick_layouts.layouts[i],
NULL, &tick_size );
if ( tick_size > max_tick_size ) {
max_tick_size = tick_size;
}
}
margin_reqs[0] += max_tick_size + 3;
if ( yldivs > context->yltick_layouts.n ) {
context->yltick_layouts.layouts = realloc(
context->yltick_layouts.layouts,
yldivs * sizeof *( context->yltick_layouts.layouts ) );
for ( int i = context->yltick_layouts.n; i < yldivs; i++ ) {
context->yltick_layouts.layouts[i] = NULL;
}
context->yltick_layouts.n = yldivs;
}
max_tick_size = 0;
for ( int i = 0; i < yldivs; i++ ) {
if ( NULL == context->yltick_layouts.layouts[i] ) {
context->yltick_layouts.layouts[i] =
pango_cairo_create_layout( cr );
pango_layout_set_font_description(
context->yltick_layouts.layouts[i],
context->font_description );
}
sprintf( tick_buffer, "%.3g", ylrange[0] +
( (double) i ) / ( yldivs - 1 ) * ( ylrange[1] - ylrange[0] ) );
pango_layout_set_text(
context->yltick_layouts.layouts[i], tick_buffer, -1 );
int tick_size;
pango_layout_get_pixel_size( context->yltick_layouts.layouts[i],
&tick_size, NULL );
if ( tick_size > max_tick_size ) {
max_tick_size = tick_size;
}
}
margin_reqs[1] += max_tick_size + 3;
if ( yrdivs > context->yrtick_layouts.n ) {
context->yrtick_layouts.layouts = realloc(
context->yrtick_layouts.layouts,
yrdivs * sizeof *( context->yrtick_layouts.layouts ) );
for ( int i = context->yrtick_layouts.n; i < yrdivs; i++ ) {
context->yrtick_layouts.layouts[i] = NULL;
}
context->yrtick_layouts.n = yrdivs;
}
max_tick_size = 0;
for ( int i = 0; i < yrdivs; i++ ) {
if ( NULL == context->yrtick_layouts.layouts[i] ) {
context->yrtick_layouts.layouts[i] =
pango_cairo_create_layout( cr );
pango_layout_set_font_description(
context->yrtick_layouts.layouts[i],
context->font_description );
}
sprintf( tick_buffer, "%.3g", yrrange[0] +
( (double) i ) / ( yrdivs - 1 ) * ( yrrange[1] - yrrange[0] ) );
pango_layout_set_text(
context->yrtick_layouts.layouts[i], tick_buffer, -1 );
int tick_size;
pango_layout_get_pixel_size( context->yrtick_layouts.layouts[i],
&tick_size, NULL );
if ( tick_size > max_tick_size ) {
max_tick_size = tick_size;
}
}
margin_reqs[3] += max_tick_size + 3;
for ( int i = 0; i < 4; i++ ) {
if ( margin_reqs[i] > context->margins[i] ) {
context->margins[i] = 1.25 * margin_reqs[i];
}
}
double* m = context->margins;
/* Axes */
cairo_save( cr );
cairo_set_source_rgb( cr, 0, 0, 0 );
cairo_set_line_width( cr, 1 );
cairo_move_to( cr, width - m[3], height - m[0] );
cairo_line_to( cr, m[1], height - m[0] );
cairo_line_to( cr, m[1], m[2] );
cairo_line_to( cr, width - m[3], m[2] );
cairo_line_to( cr, width - m[3], height - m[0] );
cairo_stroke( cr );
cairo_restore( cr );
/* Data */ /* Data */
...@@ -959,11 +1179,11 @@ static void plot_draw( GtkDrawingArea* plot, ...@@ -959,11 +1179,11 @@ static void plot_draw( GtkDrawingArea* plot,
} }
cairo_save( cr ); cairo_save( cr );
cairo_move_to( cr, 4, 4 ); cairo_move_to( cr, m[1] - 1, m[2] - 1 );
cairo_line_to( cr, width - 4, 4 ); cairo_line_to( cr, width - m[3] + 1, m[2] - 1 );
cairo_line_to( cr, width - 4, height - 4 ); cairo_line_to( cr, width - m[3] + 1, height - m[0] + 1);
cairo_line_to( cr, 4, height - 4 ); cairo_line_to( cr, m[1] - 1, height - m[0] + 1 );
cairo_line_to( cr, 4, 4 ); cairo_line_to( cr, m[1] - 1, m[2] - 1 );
cairo_clip( cr ); cairo_clip( cr );
cairo_set_line_width( cr, 1 ); cairo_set_line_width( cr, 1 );
...@@ -986,17 +1206,17 @@ static void plot_draw( GtkDrawingArea* plot, ...@@ -986,17 +1206,17 @@ static void plot_draw( GtkDrawingArea* plot,
if ( data->next->y[i].present ) { if ( data->next->y[i].present ) {
cairo_move_to( cr, cairo_move_to( cr,
5 + ( width - 10 ) * m[1] + ( width - m[1] - m[3] ) *
( data->x - xrange[0] ) / ( data->x - xrange[0] ) /
( xrange[1] - xrange[0] ), ( xrange[1] - xrange[0] ),
height - 5 - ( height - 10 ) * height - m[0] - ( height - m[0] - m[2] ) *
( data->y[i].v - yrange[0] ) / ( data->y[i].v - yrange[0] ) /
( yrange[1] - yrange[0] ) ); ( yrange[1] - yrange[0] ) );
cairo_line_to( cr, cairo_line_to( cr,
5 + ( width - 10 ) * m[1] + ( width - m[1] - m[3] ) *
( data->next->x - xrange[0] ) / ( data->next->x - xrange[0] ) /
( xrange[1] - xrange[0] ), ( xrange[1] - xrange[0] ),
height - 5 - ( height - 10 ) * height - m[0] - ( height - m[0] - m[2] ) *
( data->next->y[i].v - yrange[0] ) / ( data->next->y[i].v - yrange[0] ) /
( yrange[1] - yrange[0] ) ); ( yrange[1] - yrange[0] ) );
cairo_stroke( cr ); cairo_stroke( cr );
...@@ -1005,10 +1225,10 @@ static void plot_draw( GtkDrawingArea* plot, ...@@ -1005,10 +1225,10 @@ static void plot_draw( GtkDrawingArea* plot,
!data->prev->y[i].present ) { !data->prev->y[i].present ) {
cairo_rectangle( cr, cairo_rectangle( cr,
5 + ( width - 10 ) * m[1] + ( width - m[1] - m[3] ) *
( data->x - xrange[0] ) / ( data->x - xrange[0] ) /
( xrange[1] - xrange[0] ), ( xrange[1] - xrange[0] ),
height - 5 - ( height - 10 ) * height - m[0] - ( height - m[0] - m[2] ) *
( data->y[i].v - yrange[0] ) / ( data->y[i].v - yrange[0] ) /
( yrange[1] - yrange[0] ) - 1, 2, 2 ); ( yrange[1] - yrange[0] ) - 1, 2, 2 );
cairo_fill( cr ); cairo_fill( cr );
...@@ -1027,34 +1247,123 @@ static void plot_draw( GtkDrawingArea* plot, ...@@ -1027,34 +1247,123 @@ static void plot_draw( GtkDrawingArea* plot,
for ( int i = 0; i < xdivs; i++ ) { for ( int i = 0; i < xdivs; i++ ) {
cairo_move_to( cr, cairo_move_to( cr,
5 + i * ( width - 10 ) / ( xdivs - 1 ), m[1] + i * ( width - m[1] - m[3] ) / ( xdivs - 1 ),
height - 5 ); height - m[0] );
cairo_line_to( cr, cairo_line_to( cr,
5 + i * ( width - 10 ) / ( xdivs - 1 ), m[1] + i * ( width - m[1] - m[3] ) / ( xdivs - 1 ),
height - 2 ); height - m[0] + 3 );
cairo_stroke( cr ); cairo_stroke( cr );
int text_w;
pango_layout_get_pixel_size( context->xtick_layouts.layouts[i],
&text_w, NULL );
cairo_move_to( cr,
m[1] + i * ( width - m[1] - m[3] ) / ( xdivs - 1 ) -
text_w / 2,
height - m[0] + 5 );
pango_cairo_show_layout( cr,
context->xtick_layouts.layouts[i] );
} }
for ( int i = 0; i < yldivs && lset; i++ ) { for ( int i = 0; i < yldivs && lset; i++ ) {
cairo_move_to( cr, cairo_move_to( cr,
2, m[1] - 3,
5 + i * ( height - 10 ) / ( yldivs - 1 ) ); m[2] + i * ( height - m[2] - m[0] ) / ( yldivs - 1 ) );
cairo_line_to( cr, cairo_line_to( cr,
5, m[1],
5 + i * ( height - 10 ) / ( yldivs - 1 ) ); m[2] + i * ( height - m[2] - m[0] ) / ( yldivs - 1 ) );
cairo_stroke( cr ); cairo_stroke( cr );
int text_w;
int text_h;
pango_layout_get_pixel_size(
context->yltick_layouts.layouts[yldivs - i - 1],
&text_w, &text_h );
cairo_move_to( cr,
m[1] - 5 - text_w,
m[2] + i * ( height - m[2] - m[0] ) / ( yldivs - 1 ) -
text_h / 2 );
pango_cairo_show_layout( cr,
context->yltick_layouts.layouts[yldivs - i - 1] );
} }
for ( int i = 0; i < yrdivs && rset; i++ ) { for ( int i = 0; i < yrdivs && rset; i++ ) {
cairo_move_to( cr, cairo_move_to( cr,
width - 5, width - m[3],
5 + i * ( height - 10 ) / ( yrdivs - 1 ) ); m[2] + i * ( height - m[2] - m[0] ) / ( yrdivs - 1 ) );
cairo_line_to( cr, cairo_line_to( cr,
width - 2, width - m[3] + 3,
5 + i * ( height - 10 ) / ( yrdivs - 1 ) ); m[2] + i * ( height - m[2] - m[0] ) / ( yrdivs - 1 ) );
cairo_stroke( cr ); cairo_stroke( cr );
int text_h;
pango_layout_get_pixel_size(
context->yrtick_layouts.layouts[yrdivs - i -1],
NULL, &text_h );
cairo_move_to( cr,
width - m[3] + 5,
m[2] + i * ( height - m[2] - m[0] ) / ( yrdivs - 1 ) -
text_h / 2 );
pango_cairo_show_layout( cr,
context->yrtick_layouts.layouts[yrdivs - i - 1] );
}
cairo_restore( cr );
/* Titles */
cairo_save( cr );
if ( NULL != context->title_layout ) {
int title_w;
pango_layout_get_pixel_size( context->title_layout,
&title_w, NULL );
cairo_move_to( cr,
m[1] + ( width - m[1] - m[3] ) / 2 - title_w / 2, 3 );
pango_cairo_show_layout( cr, context->title_layout );
} }
cairo_restore( cr );
cairo_save( cr );
if ( NULL != context->xlab_layout ) {
int xlab_w;
int xlab_h;
pango_layout_get_pixel_size( context->xlab_layout,
&xlab_w, &xlab_h );
cairo_move_to( cr,
m[1] + ( width - m[1] - m[3] ) / 2 - xlab_w / 2,
height - xlab_h - 3 );
pango_cairo_show_layout( cr, context->xlab_layout );
}
cairo_restore( cr );
cairo_save( cr );
if ( NULL != context->yllab_layout ) {
int yllab_w;
pango_layout_get_pixel_size( context->yllab_layout,
&yllab_w, NULL );
cairo_move_to( cr,
3,
m[2] + ( height - m[2] - m[0] ) / 2 + yllab_w / 2 );
cairo_rotate( cr, - M_PI / 2 );
pango_cairo_show_layout( cr, context->yllab_layout );
}
cairo_restore( cr );
cairo_save( cr );
if ( NULL != context->yrlab_layout ) {
int yrlab_w;
pango_layout_get_pixel_size( context->yrlab_layout,
&yrlab_w, NULL );
cairo_move_to( cr,
width - 3,
m[2] + ( height - m[2] - m[0] ) / 2 - yrlab_w / 2 );
cairo_rotate( cr, M_PI / 2 );
pango_cairo_show_layout( cr, context->yrlab_layout );
}
cairo_restore( cr );
} }
static int pretty( double* lo, double* up, int ndiv ) { static int pretty( double* lo, double* up, int ndiv ) {
...@@ -1155,6 +1464,6 @@ static int pretty( double* lo, double* up, int ndiv ) { ...@@ -1155,6 +1464,6 @@ static int pretty( double* lo, double* up, int ndiv ) {
*up = nu * unit; *up = nu * unit;
} }
return ndiv; return ndiv + 1;
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment