2 static char *RCSid() { return RCSid("$Id: color.c,v 1.70.2.9 2008/06/23 17:51:35 mikulik Exp $"); }
5 /* GNUPLOT - color.c */
9 * Petr Mikulik, since December 1998
10 * Copyright: open source as much as possible
13 * - Global variables declared in .h are initialized here
15 * - Colour box drawing
38 /* COLOUR MODES - GLOBAL VARIABLES */
40 t_sm_palette sm_palette; /* initialized in init_color() */
42 /* Copy of palette previously in use.
43 * Exported so that change_term() can invalidate contents
44 * FIXME: better naming
46 static t_sm_palette prev_palette = {
47 -1, -1, -1, -1, -1, -1, -1, -1,
52 #ifdef EXTENDED_COLOR_SPECS
53 int supply_extended_color_specs = 0;
56 /* Corners of the colour box. */
57 static int cb_x_from, cb_x_to, cb_y_from, cb_y_to;
59 /* Internal prototype declarations: */
61 static void draw_inside_color_smooth_box_postscript __PROTO((FILE * out));
62 static void draw_inside_color_smooth_box_bitmap __PROTO((FILE * out));
63 void cbtick_callback __PROTO((AXIS_INDEX axis, double place, char *text, struct lp_style_type grid));
67 /* *******************************************************************
75 /* initialize global palette */
76 sm_palette.colorFormulae = 37; /* const */
77 sm_palette.formulaR = 7;
78 sm_palette.formulaG = 5;
79 sm_palette.formulaB = 15;
80 sm_palette.positive = SMPAL_POSITIVE;
81 sm_palette.use_maxcolors = 0;
82 sm_palette.colors = 0;
83 sm_palette.color = NULL;
84 sm_palette.ps_allcF = 0;
85 sm_palette.gradient_num = 0;
86 sm_palette.gradient = NULL;
87 sm_palette.cmodel = C_MODEL_RGB;
88 sm_palette.Afunc.at = sm_palette.Bfunc.at = sm_palette.Cfunc.at = NULL;
89 sm_palette.gamma = 1.5;
91 /* initialisation of smooth color box */
92 color_box = default_color_box;
97 Make the colour palette. Return 0 on success
98 Put number of allocated colours into sm_palette.colors
106 if (!term->make_palette) {
107 fprintf(stderr, "Error: terminal \"%s\" does not support continuous colors.\n",term->name);
111 /* ask for suitable number of colours in the palette */
112 i = term->make_palette(NULL);
114 /* terminal with its own mapping (PostScript, for instance)
115 It will not change palette passed below, but non-NULL has to be
116 passed there to create the header or force its initialization
118 if (memcmp(&prev_palette, &sm_palette, sizeof(t_sm_palette))) {
119 term->make_palette(&sm_palette);
120 prev_palette = sm_palette;
121 FPRINTF((stderr,"make_palette: calling term->make_palette for term with ncolors == 0\n"));
123 FPRINTF((stderr,"make_palette: skipping duplicate palette for term with ncolors == 0\n"));
127 /* set the number of colours to be used (allocated) */
128 sm_palette.colors = i;
129 if (sm_palette.use_maxcolors > 0 && i > sm_palette.use_maxcolors)
130 sm_palette.colors = sm_palette.use_maxcolors;
132 if (prev_palette.colorFormulae < 0
133 || sm_palette.colorFormulae != prev_palette.colorFormulae
134 || sm_palette.colorMode != prev_palette.colorMode
135 || sm_palette.formulaR != prev_palette.formulaR
136 || sm_palette.formulaG != prev_palette.formulaG
137 || sm_palette.formulaB != prev_palette.formulaB
138 || sm_palette.positive != prev_palette.positive
139 || sm_palette.colors != prev_palette.colors) {
140 /* print the message only if colors have changed */
142 fprintf(stderr, "smooth palette in %s: available %i color positions; using %i of them\n", term->name, i, sm_palette.colors);
145 prev_palette = sm_palette;
147 if (sm_palette.color != NULL) {
148 free(sm_palette.color);
149 sm_palette.color = NULL;
151 sm_palette.color = gp_alloc( sm_palette.colors * sizeof(rgb_color),
152 "pm3d palette color");
154 /* fill sm_palette.color[] */
155 for (i = 0; i < sm_palette.colors; i++) {
156 gray = (double) i / (sm_palette.colors - 1); /* rescale to [0;1] */
157 rgb1_from_gray( gray, &(sm_palette.color[i]) );
160 /* let the terminal make the palette from the supplied RGB triplets */
161 term->make_palette(&sm_palette);
167 * Force a mismatch between the current palette and whatever is sent next,
168 * so that the new one will always be loaded
173 prev_palette.colors = -1;
177 Set the colour on the terminal
178 Currently, each terminal takes care of remembering the current colour,
179 so there is not much to do here.
182 set_color(double gray)
185 if (!(term->set_color))
187 color.type = TC_FRAC;
189 term->set_color(&color);
193 set_rgbcolor(int rgblt)
196 if (!(term->set_color))
200 term->set_color(&color);
203 void ifilled_quadrangle(gpiPoint* icorners)
205 icorners->style = FS_OPAQUE;
206 term->filled_polygon(4, icorners);
208 if (pm3d.hidden3d_tag) {
212 /* Colour has changed, so we must apply line properties again.
213 * FIXME: It would be cleaner to apply the general line properties
214 * outside this loop, and limit ourselves to apply_pm3dcolor().
216 static struct lp_style_type lp = DEFAULT_LP_STYLE_TYPE;
217 lp_use_properties(&lp, pm3d.hidden3d_tag, 0);
218 term_apply_lp_properties(&lp);
220 term->move(icorners[0].x, icorners[0].y);
221 for (i = 3; i >= 0; i--) {
222 term->vector(icorners[i].x, icorners[i].y);
228 /* The routine above for 4 points explicitly.
229 * This is the only routine which supportes extended
230 * color specs currently.
232 #ifdef EXTENDED_COLOR_SPECS
234 filled_quadrangle(gpdPoint * corners, gpiPoint * icorners)
237 filled_quadrangle(gpdPoint * corners)
242 #ifndef EXTENDED_COLOR_SPECS
243 gpiPoint icorners[4];
245 for (i = 0; i < 4; i++) {
246 map3d_xy_double(corners[i].x, corners[i].y, corners[i].z, &x, &y);
251 ifilled_quadrangle(icorners);
256 Makes mapping from real 3D coordinates, passed as coords array,
257 to 2D terminal coordinates, then draws filled polygon
260 filled_polygon_3dcoords(int points, struct coordinate GPHUGE * coords)
265 icorners = gp_alloc(points * sizeof(gpiPoint), "filled_polygon3d corners");
266 for (i = 0; i < points; i++) {
267 map3d_xy_double(coords[i].x, coords[i].y, coords[i].z, &x, &y);
271 #ifdef EXTENDED_COLOR_SPECS
272 if (supply_extended_color_specs) {
273 icorners[0].spec.gray = -1; /* force solid color */
276 icorners->style = FS_OPAQUE;
277 term->filled_polygon(points, icorners);
283 Makes mapping from real 3D coordinates, passed as coords array, but at z coordinate
284 fixed (base_z, for instance) to 2D terminal coordinates, then draws filled polygon
287 filled_polygon_3dcoords_zfixed(int points, struct coordinate GPHUGE * coords, double z)
292 icorners = gp_alloc(points * sizeof(gpiPoint), "filled_polygon_zfix corners");
293 for (i = 0; i < points; i++) {
294 map3d_xy_double(coords[i].x, coords[i].y, z, &x, &y);
298 #ifdef EXTENDED_COLOR_SPECS
299 if (supply_extended_color_specs) {
300 icorners[0].spec.gray = -1; /* force solid color */
303 icorners->style = FS_OPAQUE;
304 term->filled_polygon(points, icorners);
310 Draw colour smooth box
312 Firstly two helper routines for plotting inside of the box
313 for postscript and for other terminals, finally the main routine
317 /* plot the colour smooth box for from terminal's integer coordinates
318 [cb_x_from,cb_y_from] to [cb_x_to,cb_y_to].
319 This routine is for postscript files --- actually, it writes a small
323 draw_inside_color_smooth_box_postscript(FILE * out)
325 int scale_x = (cb_x_to - cb_x_from), scale_y = (cb_y_to - cb_y_from);
326 fputs("stroke gsave\t%% draw gray scale smooth box\n"
327 "maxcolors 0 gt {/imax maxcolors def} {/imax 1024 def} ifelse\n", out);
329 /* nb. of discrete steps (counted in the loop) */
330 fprintf(out, "%i %i translate %i %i scale 0 setlinewidth\n", cb_x_from, cb_y_from, scale_x, scale_y);
331 /* define left bottom corner and scale of the box so that all coordinates
332 of the box are from [0,0] up to [1,1]. Further, this normalization
333 makes it possible to pass y from [0,1] as parameter to setgray */
334 fprintf(out, "/ystep 1 imax div def /y0 0 def /ii 0 def\n");
335 /* local variables; y-step, current y position and counter ii; */
336 if (sm_palette.positive == SMPAL_NEGATIVE) /* inverted gray for negative figure */
337 fputs("{ 0.99999 y0 sub g ", out); /* 1 > x > 1-1.0/1024 */
339 fputs("{ y0 g ", out);
340 if (color_box.rotation == 'v')
341 fputs("0 y0 N 1 0 V 0 ystep V -1 0 f\n", out);
343 fputs("y0 0 N 0 1 V ystep 0 V 0 -1 f\n", out);
344 fputs("/y0 y0 ystep add def /ii ii 1 add def\n"
345 "ii imax ge {exit} if } loop\n"
346 "grestore 0 setgray\n", out);
351 /* plot the colour smooth box for from terminal's integer coordinates
352 [x_from,y_from] to [x_to,y_to].
353 This routine is for non-postscript files, as it does explicitly the loop
354 over all thin rectangles
357 draw_inside_color_smooth_box_bitmap(FILE * out)
359 int steps = 128; /* I think that nobody can distinguish more colours drawn in the palette */
360 int i, xy, xy2, xy_from, xy_to;
361 double xy_step, gray;
364 (void) out; /* to avoid "unused parameter" warning */
365 if (color_box.rotation == 'v') {
366 corners[0].x = corners[3].x = cb_x_from;
367 corners[1].x = corners[2].x = cb_x_to;
371 corners[0].y = corners[1].y = cb_y_from;
372 corners[2].y = corners[3].y = cb_y_to;
376 xy_step = (color_box.rotation == 'h' ? cb_x_to - cb_x_from : cb_y_to - cb_y_from) / (double) steps;
378 for (i = 0; i < steps; i++) {
379 gray = (double) i / steps; /* colours equidistantly from [0,1] */
380 if (sm_palette.positive == SMPAL_NEGATIVE)
382 /* Set the colour (also for terminals which support extended specs). */
384 xy = xy_from + (int) (xy_step * i);
385 xy2 = xy_from + (int) (xy_step * (i + 1));
386 if (color_box.rotation == 'v') {
387 corners[0].y = corners[1].y = xy;
388 corners[2].y = corners[3].y = (i == steps - 1) ? xy_to : xy2;
390 corners[0].x = corners[3].x = xy;
391 corners[1].x = corners[2].x = (i == steps - 1) ? xy_to : xy2;
393 #ifdef EXTENDED_COLOR_SPECS
394 if (supply_extended_color_specs) {
395 corners[0].spec.gray = -1; /* force solid color */
398 /* print the rectangle with the given colour */
399 corners->style = FS_OPAQUE;
400 term->filled_polygon(4, corners);
404 /* Notice HBB 20010720: would be static, but HP-UX gcc bug forbids
411 struct lp_style_type grid) /* linetype or -2 for no grid */
413 int len = (text ? CB_AXIS.ticscale : CB_AXIS.miniticscale)
414 * (CB_AXIS.tic_in ? -1 : 1) * (term->h_tic);
415 double cb_place = (place - CB_AXIS.min) / (CB_AXIS.max - CB_AXIS.min);
416 /* relative z position along the colorbox axis */
417 unsigned int x1, y1, x2, y2;
420 printf("cbtick_callback: place=%g\ttext=\"%s\"\tgrid.l_type=%i\n",place,text,grid.l_type);
422 (void) axis; /* to avoid 'unused' warning */
424 /* calculate tic position */
425 if (color_box.rotation == 'h') {
426 x1 = x2 = cb_x_from + cb_place * (cb_x_to - cb_x_from);
428 y2 = cb_y_from - len;
432 y1 = y2 = cb_y_from + cb_place * (cb_y_to - cb_y_from);
436 if (grid.l_type > LT_NODRAW) {
437 term_apply_lp_properties(&grid); /* grid linetype */
438 if (color_box.rotation == 'h') {
439 (*term->move) (x1, cb_y_from);
440 (*term->vector) (x1, cb_y_to);
442 (*term->move) (cb_x_from, y1);
443 (*term->vector) (cb_x_to, y1);
445 term_apply_lp_properties(&border_lp); /* border linetype */
449 (*term->move) (x1, y1);
450 (*term->vector) (x2, y2);
455 int offsetx, offsety;
456 map3d_position_r(&(axis_array[axis].ticdef.offset),
457 &offsetx, &offsety, "cbtics");
458 /* User-specified different color for the tics text */
459 if (axis_array[axis].ticdef.textcolor.type != TC_DEFAULT)
460 apply_pm3dcolor(&(axis_array[axis].ticdef.textcolor), term);
461 if (color_box.rotation == 'h') {
462 int y3 = cb_y_from - (term->v_char);
465 if (axis_array[axis].tic_rotate
466 && (*term->text_angle)(axis_array[axis].tic_rotate))
467 hrotate = axis_array[axis].tic_rotate;
468 if (len > 0) y3 -= len; /* add outer tics len */
470 write_multiline(x2+offsetx, y3+offsety, text,
471 (hrotate ? LEFT : CENTRE), CENTRE, hrotate,
472 axis_array[axis].ticdef.font);
474 (*term->text_angle)(0);
476 unsigned int x3 = cb_x_to + (term->h_char);
477 if (len > 0) x3 += len; /* add outer tics len */
478 write_multiline(x3+offsetx, y2+offsety, text,
480 axis_array[axis].ticdef.font);
482 term_apply_lp_properties(&border_lp); /* border linetype */
485 /* draw tic on the mirror side */
486 if (CB_AXIS.ticmode & TICS_MIRROR) {
487 if (color_box.rotation == 'h') {
492 x2 = cb_x_from - len;
494 (*term->move) (x1, y1);
495 (*term->vector) (x2, y2);
500 Finally the main colour smooth box drawing routine
503 draw_color_smooth_box(int plot_mode)
506 FILE *out = gppsfile; /* either gpoutfile or PSLATEX_auxfile */
508 if (color_box.where == SMCOLOR_BOX_NO)
510 if (!term->filled_polygon)
514 firstly, choose some good position of the color box
516 user's position like that (?):
518 x_from = color_box.xlow;
519 x_to = color_box.xhigh;
522 if (color_box.where == SMCOLOR_BOX_USER) {
525 map_position(&color_box.origin, &cb_x_from, &cb_y_from, "cbox");
526 map_position_r(&color_box.size, &xtemp, &ytemp, "cbox");
529 } else if (splot_map && is_3d_plot) {
530 /* In map view mode we allow any coordinate system for placement */
532 map3d_position_double(&color_box.origin, &xtemp, &ytemp, "cbox");
535 map3d_position_r(&color_box.size, &cb_x_to, &cb_y_to, "cbox");
537 /* But in full 3D mode we only allow screen coordinates */
538 cb_x_from = color_box.origin.x * (term->xmax) + 0.5;
539 cb_y_from = color_box.origin.y * (term->ymax) + 0.5;
540 cb_x_to = color_box.size.x * (term->xmax) + 0.5;
541 cb_y_to = color_box.size.y * (term->ymax) + 0.5;
543 cb_x_to += cb_x_from;
544 cb_y_to += cb_y_from;
546 } else { /* color_box.where == SMCOLOR_BOX_DEFAULT */
547 if (plot_mode == MODE_SPLOT && !splot_map) {
548 /* HBB 20031215: new code. Constants fixed to what the result
549 * of the old code in default view (set view 60,30,1,1)
550 * happened to be. Somebody fix them if they're not right! */
551 cb_x_from = xmiddle + 0.709 * xscaler;
552 cb_x_to = xmiddle + 0.778 * xscaler;
553 cb_y_from = ymiddle - 0.147 * yscaler;
554 cb_y_to = ymiddle + 0.497 * yscaler;
556 } else if (is_3d_plot) {
557 /* MWS 09-Dec-05, make color box full size for splot maps. */
558 double dx = (X_AXIS.max - X_AXIS.min);
559 map3d_xy(X_AXIS.max + dx * 0.025, Y_AXIS.min, base_z, &cb_x_from, &cb_y_from);
560 map3d_xy(X_AXIS.max + dx * 0.075, Y_AXIS.max, ceiling_z, &cb_x_to, &cb_y_to);
561 } else { /* 2D plot */
562 struct position default_origin = {graph,graph,graph, 1.025, 0, 0};
563 struct position default_size = {graph,graph,graph, 0.05, 1.0, 0};
565 map_position(&default_origin, &cb_x_from, &cb_y_from, "cbox");
566 map_position_r(&default_size, &xtemp, &ytemp, "cbox");
567 cb_x_to = xtemp + cb_x_from;
568 cb_y_to = ytemp + cb_y_from;
571 /* now corrections for outer tics */
572 if (color_box.rotation == 'v') {
573 int cblen = (CB_AXIS.tic_in ? -1 : 1) * CB_AXIS.ticscale *
574 (term->h_tic); /* positive for outer tics */
575 int ylen = (Y_AXIS.tic_in ? -1 : 1) * Y_AXIS.ticscale *
576 (term->h_tic); /* positive for outer tics */
577 if ((cblen > 0) && (CB_AXIS.ticmode & TICS_MIRROR)) {
582 (axis_array[FIRST_Y_AXIS].ticmode & TICS_MIRROR)) {
589 if (cb_y_from > cb_y_to) { /* switch them */
595 /* Optimized version of the smooth colour box in postscript. Advantage:
596 only few lines of code is written into the output file.
599 draw_inside_color_smooth_box_postscript(out);
601 draw_inside_color_smooth_box_bitmap(out);
603 if (color_box.border) {
604 /* now make boundary around the colour box */
605 if (color_box.border_lt_tag >= 0) {
606 /* user specified line type */
607 struct lp_style_type lp = border_lp;
608 lp_use_properties(&lp, color_box.border_lt_tag, 1);
609 term_apply_lp_properties(&lp);
611 /* black solid colour should be chosen, so it's border linetype */
612 term_apply_lp_properties(&border_lp);
615 (term->move) (cb_x_from, cb_y_from);
616 (term->vector) (cb_x_to, cb_y_from);
617 (term->vector) (cb_x_to, cb_y_to);
618 (term->vector) (cb_x_from, cb_y_to);
619 (term->vector) (cb_x_from, cb_y_from);
622 /* Set line properties to some value, this also draws lines in postscript terminals. */
623 term_apply_lp_properties(&border_lp);
627 if (axis_array[COLOR_AXIS].ticmode) {
628 term_apply_lp_properties(&border_lp); /* border linetype */
629 gen_tics(COLOR_AXIS, cbtick_callback );
632 /* write the colour box label */
633 if (CB_AXIS.label.text) {
635 apply_pm3dcolor(&(CB_AXIS.label.textcolor),term);
636 if (color_box.rotation == 'h') {
637 int len = CB_AXIS.ticscale * (CB_AXIS.tic_in ? 1 : -1) *
640 map3d_position_r(&(CB_AXIS.label.offset), &x, &y, "smooth_box");
641 x += (cb_x_from + cb_x_to) / 2;
643 #define DEFAULT_Y_DISTANCE 1.0
644 y += cb_y_from + (- DEFAULT_Y_DISTANCE - 1.7) * term->v_char;
645 #undef DEFAULT_Y_DISTANCE
646 if (len < 0) y += len;
649 write_multiline(x, y, CB_AXIS.label.text, CENTRE, JUST_CENTRE, 0,
652 int len = CB_AXIS.ticscale * (CB_AXIS.tic_in ? -1 : 1) *
654 /* calculate max length of cb-tics labels */
655 widest_tic_strlen = 0;
656 if (CB_AXIS.ticmode & TICS_ON_BORDER) {
657 widest_tic_strlen = 0; /* reset the global variable */
658 gen_tics(COLOR_AXIS, /* 0, */ widest_tic_callback);
660 map3d_position_r(&(CB_AXIS.label.offset), &x, &y, "smooth_box");
661 #define DEFAULT_X_DISTANCE 1.0
662 x += cb_x_to + (widest_tic_strlen + DEFAULT_X_DISTANCE + 1.5) * term->h_char;
663 #undef DEFAULT_X_DISTANCE
664 if (len > 0) x += len;
665 y += (cb_y_from + cb_y_to) / 2;
668 if ((*term->text_angle)(CB_AXIS.label.rotate)) {
669 write_multiline(x, y, CB_AXIS.label.text, CENTRE, JUST_TOP,
670 CB_AXIS.label.rotate, CB_AXIS.label.font);
671 (*term->text_angle)(0);
673 write_multiline(x, y, CB_AXIS.label.text, LEFT, JUST_TOP, 0, CB_AXIS.label.font);
676 reset_textcolor(&(CB_AXIS.label.textcolor),term);