Skip to content
Snippets Groups Projects
Commit 15e52d12 authored by tag2y19's avatar tag2y19
Browse files

Add colour options

Colour options come from the command line (-c) or the EPLOT_COLOURS
environment variable or the hard coded default, in that order.

The format is '#RRGGBB[,(solid|dash|dot|dotdash)][,width][:#R2G2B2...]'.
Width defaults to 1 and the style to solid.  If the -c option is given
multiple times, the values are concatenated with ':'.
parent 845323cc
Branches
No related tags found
No related merge requests found
...@@ -30,6 +30,15 @@ struct data { ...@@ -30,6 +30,15 @@ struct data {
}; };
struct colour {
float colour[3];
int width;
const double* dash;
int n_dashes;
};
struct context { struct context {
char* read_buffer; char* read_buffer;
...@@ -86,7 +95,8 @@ struct context { ...@@ -86,7 +95,8 @@ struct context {
char* xaxis; char* xaxis;
size_t xaxis_n; size_t xaxis_n;
float (* colours)[3]; char* colour_args;
struct colour* colours;
size_t n_colours; size_t n_colours;
double xdist; double xdist;
...@@ -120,7 +130,10 @@ struct fd_source { ...@@ -120,7 +130,10 @@ struct fd_source {
static bool add_to_column_list( const char* option_name, static bool add_to_column_list( const char* option_name,
const char* value, void* data, GError** error ); const char* value, void* data, GError** error );
static int parse_hex( float res[3], const char* hex ); static bool add_colour( const char* option_name,
const char* value, void* data, GError** error );
static char* parse_hex( float res[3], const char* hex );
static void get_colours( struct context* context );
static void app_activate( GtkApplication* app, void* data ); static void app_activate( GtkApplication* app, void* data );
static int listener_prepare( GSource* source, int* timeout ); static int listener_prepare( GSource* source, int* timeout );
static int listener_check( GSource* source ); static int listener_check( GSource* source );
...@@ -149,18 +162,6 @@ int main( int argc, char** argv ) { ...@@ -149,18 +162,6 @@ int main( int argc, char** argv ) {
context->font_size = 12; context->font_size = 12;
context->show_legend = true; context->show_legend = true;
static const char* colours[] = {
"#005C84", "#FCBC00", "#0C838C", "#E63037",
"#74E9C5", "#D5007F", "#83DBD2", "#4BB694",
"#8D3970", "#002F4C", "#EF7D00", "#C0D100",
};
context->n_colours = sizeof colours / sizeof *colours;
context->colours = calloc( sizeof colours / sizeof *colours,
sizeof *( context->colours ) );
for ( size_t i = 0; i < sizeof colours / sizeof *colours; i++ ) {
assert( 0 == parse_hex( context->colours[i], colours[i] ) );
}
clock_gettime( CLOCK_MONOTONIC, &( context->start_time ) ); clock_gettime( CLOCK_MONOTONIC, &( context->start_time ) );
fcntl( STDIN_FILENO, F_SETFL, fcntl( STDIN_FILENO, F_SETFL,
...@@ -264,6 +265,14 @@ int main( int argc, char** argv ) { ...@@ -264,6 +265,14 @@ int main( int argc, char** argv ) {
.arg_data = &( context->yrlab ), .arg_data = &( context->yrlab ),
.description = "Right y-axis label", .description = "Right y-axis label",
.arg_description = "label", .arg_description = "label",
}, {
.long_name = "colour",
.short_name = 'c',
.flags = G_OPTION_FLAG_IN_MAIN,
.arg = G_OPTION_ARG_CALLBACK,
.arg_data = &add_colour,
.description = "Add line colour",
.arg_description = "colour"
}, { }, {
.long_name = "time", .long_name = "time",
.short_name = 'T', .short_name = 'T',
...@@ -324,7 +333,38 @@ static bool add_to_column_list( const char* option_name, ...@@ -324,7 +333,38 @@ static bool add_to_column_list( const char* option_name,
} }
static int parse_hex( float res[3], const char* hex ) { static bool add_colour( const char* option_name,
const char* value, void* data, GError** error ) {
struct context* context = (struct context*) data;
(void) error;
assert( 0 == strcmp( option_name, "-c" ) ||
0 == strcmp( option_name, "--colour" ) );
size_t len = strlen( value );
if ( NULL != context->colour_args ) {
len += 1 + strlen( context->colour_args );
}
char* colours = calloc( len + 1, 1 );
if ( NULL != context->colour_args ) {
strcpy( colours, context->colour_args );
strcat( colours, ":" );
strcat( colours, value );
free( context->colour_args );
} else {
strcpy( colours, value );
}
context->colour_args = colours;
return true;
}
static char* parse_hex( float res[3], const char* hex ) {
while ( ' ' == *hex || '\t' == *hex || '#' == *hex ) { while ( ' ' == *hex || '\t' == *hex || '#' == *hex ) {
hex++; hex++;
...@@ -335,12 +375,12 @@ static int parse_hex( float res[3], const char* hex ) { ...@@ -335,12 +375,12 @@ static int parse_hex( float res[3], const char* hex ) {
if ( !( ( hex[0] >= '0' && hex[0] <= '9' ) || if ( !( ( hex[0] >= '0' && hex[0] <= '9' ) ||
( tolower( hex[0] ) >= 'a' && ( tolower( hex[0] ) >= 'a' &&
tolower( hex[0] ) <= 'f' ) ) ) { tolower( hex[0] ) <= 'f' ) ) ) {
return -1; return NULL;
} }
if ( !( ( hex[1] >= '0' && hex[1] <= '9' ) || if ( !( ( hex[1] >= '0' && hex[1] <= '9' ) ||
( tolower( hex[1] ) >= 'a' && ( tolower( hex[1] ) >= 'a' &&
tolower( hex[1] ) <= 'f' ) ) ) { tolower( hex[1] ) <= 'f' ) ) ) {
return -1; return NULL;
} }
res[i] = ( 0x10 * ( hex[0] <= '9' ? res[i] = ( 0x10 * ( hex[0] <= '9' ?
...@@ -353,7 +393,192 @@ static int parse_hex( float res[3], const char* hex ) { ...@@ -353,7 +393,192 @@ static int parse_hex( float res[3], const char* hex ) {
} }
return 0; return (char*) hex;
}
static void get_colours( struct context* context ) {
const char* colours;
static const char* default_colours =
"#005C84:"
"#FCBC00:"
"#8D3970:"
"#4BB694:"
"#E73037:"
"#74C9E5:"
"#005C84,dash:"
"#FCBC00,dash:"
"#8D3970,dash:"
"#4BB694,dash:"
"#E73037,dash:"
"#74C9E5,dash:"
"#005C84,dashdot:"
"#FCBC00,dashdot:"
"#8D3970,dashdot:"
"#4BB694,dashdot:"
"#E73037,dashdot:"
"#74C9E5,dashdot:"
"#005C84,dot:"
"#FCBC00,dot:"
"#8D3970,dot:"
"#4BB694,dot:"
"#E73037,dot:"
"#74C9E5,dot";
static const int n_default_colours = 24;
if ( NULL != context->colour_args ) {
colours = context->colour_args;
context->n_colours = 1;
for ( size_t i = 0; '\0' != colours[i]; i++ ) {
if ( ':' == colours[i] ) {
context->n_colours++;
}
}
} else if ( NULL != getenv( "EPLOT_COLOURS" ) ) {
colours = getenv( "EPLOT_COLOURS" );
context->n_colours = 1;
for ( size_t i = 0; '\0' != colours[i]; i++ ) {
if ( ':' == colours[i] ) {
context->n_colours++;
}
}
} else {
colours = default_colours;
context->n_colours = n_default_colours;
}
context->colours = calloc(
context->n_colours, sizeof *( context->colours ) );
int n = 0;
size_t i = 0;
while ( '\0' != colours[i] ) {
int error = 0;
context->colours[n].width = 1;
context->colours[n].n_dashes = 0;
while ( '\t' == *colours || ' ' == *colours ||
'\n' == *colours ) {
i++;
}
char* end = parse_hex(
context->colours[n].colour, &( colours[i] ) );
if ( NULL == end ) {
assert( colours != default_colours );
fprintf( stderr, "Invalid hex code for colour %d\n", n );
colours = default_colours;
context->n_colours = n_default_colours;
context->colours = realloc( context->colours,
context->n_colours * sizeof *( context->colours ) );
n = 0;
i = 0;
continue;
}
i = end - colours;
while ( '\t' == *colours || ' ' == *colours ||
'\n' == *colours ) {
i++;
}
while ( ',' == colours[i] ) {
i++;
while ( '\t' == *colours || ' ' == *colours ||
'\n' == *colours ) {
i++;
}
if ( '0' <= colours[i] && '9' >= colours[i] ) {
context->colours[n].width =
strtol( &( colours[i] ), &end, 10 );
i = end - colours;
} else {
if ( 0 == strncmp( &( colours[i] ), "solid", 5 ) ) {
context->colours[n].n_dashes = 0;
i += 5;
} else if ( 0 == strncmp(
&( colours[i] ), "dashdot", 7 ) ) {
static const double dashdot[] = { 10, 5, 1, 5, };
context->colours[n].dash = dashdot;
context->colours[n].n_dashes =
sizeof dashdot / sizeof *dashdot;
i += 7;
} else if ( 0 == strncmp(
&( colours[i] ), "dash", 4 ) ) {
static const double dash[] = { 10, 5, };
context->colours[n].dash = dash;
context->colours[n].n_dashes =
sizeof dash / sizeof *dash;
i += 4;
} else if ( 0 == strncmp(
&( colours[i] ), "dot", 3 ) ) {
static const double dot[] = { 0, 5, };
context->colours[n].dash = dot;
context->colours[n].n_dashes =
sizeof dot / sizeof *dot;
i += 3;
}
}
while ( '\t' == colours[i] || ' ' == colours[i] ||
'\n' == colours[i] ) {
i++;
}
if ( '\0' != colours[i] && ':' != colours[i] &&
',' != colours[i] ) {
assert( colours != default_colours );
fprintf( stderr,
"Invalid colour specification for colour %d\n", n );
colours = default_colours;
context->n_colours = n_default_colours;
context->colours = realloc( context->colours,
context->n_colours * sizeof *( context->colours ) );
n = 0;
i = 0;
error = 1;
break;
}
}
if ( 0 != error ) {
error = 0;
continue;
}
if ( ':' == colours[i] ) {
i++;
} else {
assert( '\0' == colours[i] );
}
n++;
}
} }
...@@ -390,6 +615,8 @@ static void app_activate( GtkApplication* app, void* data ) { ...@@ -390,6 +615,8 @@ static void app_activate( GtkApplication* app, void* data ) {
g_source_set_callback( g_source_set_callback(
(GSource*) source, &data_callback, context, NULL ); (GSource*) source, &data_callback, context, NULL );
get_colours( context );
char* yrange = context->yrange.arg; char* yrange = context->yrange.arg;
if ( NULL != yrange ) { if ( NULL != yrange ) {
...@@ -1402,33 +1629,45 @@ static void plot_draw( GtkDrawingArea* plot, ...@@ -1402,33 +1629,45 @@ static void plot_draw( GtkDrawingArea* plot,
cairo_line_to( cr, m[1] - 1, height - m[0] + 1 ); cairo_line_to( cr, m[1] - 1, height - m[0] + 1 );
cairo_line_to( cr, m[1] - 1, m[2] - 1 ); cairo_line_to( cr, m[1] - 1, m[2] - 1 );
cairo_clip( cr ); cairo_clip( cr );
cairo_set_line_width( cr, 1 );
for ( struct data* data = context->data;
NULL != data->next;
data = data->next ) {
for ( size_t i = 0; i < context->n_data_columns; i++ ) { for ( size_t i = 0; i < context->n_data_columns; i++ ) {
size_t c = i % context->n_colours; size_t c = i % context->n_colours;
cairo_set_source_rgb( cr, context->colours[c][0], cairo_save( cr );
context->colours[c][1], context->colours[c][2] );
cairo_set_source_rgb( cr,
context->colours[c].colour[0],
context->colours[c].colour[1],
context->colours[c].colour[2] );
cairo_set_line_width( cr, context->colours[c].width );
cairo_set_dash( cr, context->colours[c].dash,
context->colours[c].n_dashes, 0 );
cairo_set_line_cap( cr, CAIRO_LINE_CAP_ROUND );
double* yrange = context->data->y[i].axis == LEFT ? double* yrange = context->data->y[i].axis == LEFT ?
ylrange : yrrange; ylrange : yrrange;
if ( data->y[i].present ) { if ( context->data->y[i].present ) {
if ( data->next->y[i].present ) {
cairo_move_to( cr, cairo_move_to( cr,
m[1] + ( width - m[1] - m[3] ) * m[1] + ( width - m[1] - m[3] ) *
( data->x - xrange[0] ) / ( context->data->x - xrange[0] ) /
( xrange[1] - xrange[0] ), ( xrange[1] - xrange[0] ),
height - m[0] - ( height - m[0] - m[2] ) * height - m[0] - ( height - m[0] - m[2] ) *
( data->y[i].v - yrange[0] ) / ( context->data->y[i].v - yrange[0] ) /
( yrange[1] - yrange[0] ) ); ( yrange[1] - yrange[0] ) );
}
for ( struct data* data = context->data;
NULL != data;
data = data->next ) {
if ( data->y[i].present ) {
if ( NULL != data->next && data->next->y[i].present ) {
cairo_line_to( cr, cairo_line_to( cr,
m[1] + ( width - m[1] - m[3] ) * m[1] + ( width - m[1] - m[3] ) *
( data->next->x - xrange[0] ) / ( data->next->x - xrange[0] ) /
...@@ -1436,7 +1675,6 @@ static void plot_draw( GtkDrawingArea* plot, ...@@ -1436,7 +1675,6 @@ static void plot_draw( GtkDrawingArea* plot,
height - m[0] - ( height - m[0] - m[2] ) * 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 );
} else if ( NULL == data->prev || } else if ( NULL == data->prev ||
!data->prev->y[i].present ) { !data->prev->y[i].present ) {
...@@ -1450,12 +1688,32 @@ static void plot_draw( GtkDrawingArea* plot, ...@@ -1450,12 +1688,32 @@ static void plot_draw( GtkDrawingArea* plot,
( yrange[1] - yrange[0] ) - 1, 2, 2 ); ( yrange[1] - yrange[0] ) - 1, 2, 2 );
cairo_fill( cr ); cairo_fill( cr );
} else {
cairo_stroke( cr );
}
} else {
if ( NULL != data->next && data->next->y[i].present ) {
cairo_move_to( cr,
m[1] + ( width - m[1] - m[3] ) *
( data->next->x - xrange[0] ) /
( xrange[1] - xrange[0] ),
height - m[0] - ( height - m[0] - m[2] ) *
( data->next->y[i].v - yrange[0] ) /
( yrange[1] - yrange[0] ) );
} }
} }
} }
cairo_restore( cr );
} }
cairo_restore( cr ); cairo_restore( cr );
...@@ -1671,9 +1929,14 @@ static void plot_draw( GtkDrawingArea* plot, ...@@ -1671,9 +1929,14 @@ static void plot_draw( GtkDrawingArea* plot,
} }
cairo_save( cr ); cairo_save( cr );
cairo_set_source_rgb( cr, context->colours[c][0], cairo_set_source_rgb( cr,
context->colours[c][1], context->colours[c][2]); context->colours[c].colour[0],
cairo_set_line_width( cr, 1 ); context->colours[c].colour[1],
context->colours[c].colour[2] );
cairo_set_line_width( cr, context->colours[c].width );
cairo_set_dash( cr, context->colours[c].dash,
context->colours[c].n_dashes, 0 );
cairo_set_line_cap( cr, CAIRO_LINE_CAP_ROUND );
cairo_stroke( cr ); cairo_stroke( cr );
cairo_restore( cr ); cairo_restore( cr );
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment