1 /* GNUPLOT - color.c */
2 
3 /*[
4  *
5  * Petr Mikulik, since December 1998
6  * Copyright: open source as much as possible
7  *
8  * What is here:
9  *   - Global variables declared in .h are initialized here
10  *   - Palette routines
11  *   - Colour box drawing
12  *
13 ]*/
14 
15 
16 #ifdef HAVE_CONFIG_H
17 #include "config.h"
18 #endif
19 
20 #include "color.h"
21 #include "getcolor.h"
22 
23 #include "axis.h"
24 #include "gadgets.h"
25 #include "graphics.h"
26 #include "plot.h"
27 #include "graph3d.h"
28 #include "pm3d.h"
29 #include "term_api.h"
30 #include "util3d.h"
31 #include "alloc.h"
32 
33 /* COLOUR MODES - GLOBAL VARIABLES */
34 
35 t_sm_palette sm_palette;  /* initialized in plot.c on program entry */
36 
37 /* Copy of palette previously in use.
38  * Exported so that change_term() can invalidate contents
39  * FIXME: better naming
40  */
41 static t_sm_palette prev_palette = {
42 	-1, -1, -1, -1, -1, -1, -1, -1,
43 	(rgb_color *) 0, -1
44     };
45 
46 /* Internal prototype declarations: */
47 
48 static void draw_inside_color_smooth_box_postscript __PROTO((void));
49 static void draw_inside_color_smooth_box_bitmap __PROTO((void));
50 static void cbtick_callback __PROTO((struct axis *, double place, char *text, int ticlevel,
51 			struct lp_style_type grid, struct ticmark *userlabels));
52 
53 
54 
55 /* *******************************************************************
56   ROUTINES
57  */
58 
59 
60 void
init_color()61 init_color()
62 {
63   /* initialize global palette */
64   sm_palette.colorFormulae = 37;  /* const */
65   sm_palette.formulaR = 7;
66   sm_palette.formulaG = 5;
67   sm_palette.formulaB = 15;
68   sm_palette.positive = SMPAL_POSITIVE;
69   sm_palette.use_maxcolors = 0;
70   sm_palette.colors = 0;
71   sm_palette.color = NULL;
72   sm_palette.ps_allcF = FALSE;
73   sm_palette.gradient_num = 0;
74   sm_palette.gradient = NULL;
75   sm_palette.cmodel = C_MODEL_RGB;
76   sm_palette.Afunc.at = sm_palette.Bfunc.at = sm_palette.Cfunc.at = NULL;
77   sm_palette.colorMode = SMPAL_COLOR_MODE_RGB;
78   sm_palette.gamma = 1.5;
79 }
80 
81 
82 /*
83    Make the colour palette. Return 0 on success
84    Put number of allocated colours into sm_palette.colors
85  */
86 int
make_palette()87 make_palette()
88 {
89     int i;
90     double gray;
91 
92     if (!term->make_palette) {
93 	return 1;
94     }
95 
96     /* ask for suitable number of colours in the palette */
97     i = term->make_palette(NULL);
98     sm_palette.colors = i;
99     if (i == 0) {
100 	/* terminal with its own mapping (PostScript, for instance)
101 	   It will not change palette passed below, but non-NULL has to be
102 	   passed there to create the header or force its initialization
103 	 */
104 	if (memcmp(&prev_palette, &sm_palette, sizeof(t_sm_palette))) {
105 	    term->make_palette(&sm_palette);
106 	    prev_palette = sm_palette;
107 	    FPRINTF((stderr,"make_palette: calling term->make_palette for term with ncolors == 0\n"));
108 	} else {
109 	    FPRINTF((stderr,"make_palette: skipping duplicate palette for term with ncolors == 0\n"));
110 	}
111 	return 0;
112     }
113 
114     /* set the number of colours to be used (allocated) */
115     if (sm_palette.use_maxcolors > 0) {
116 	if (sm_palette.colorMode == SMPAL_COLOR_MODE_GRADIENT)
117 	    sm_palette.colors = i;	/* EAM Sep 2010 - could this be a constant? */
118 	else if (i > sm_palette.use_maxcolors)
119 	    sm_palette.colors = sm_palette.use_maxcolors;
120     }
121 
122     if (prev_palette.colorFormulae < 0
123 	|| sm_palette.colorFormulae != prev_palette.colorFormulae
124 	|| sm_palette.colorMode != prev_palette.colorMode
125 	|| sm_palette.formulaR != prev_palette.formulaR
126 	|| sm_palette.formulaG != prev_palette.formulaG
127 	|| sm_palette.formulaB != prev_palette.formulaB
128 	|| sm_palette.positive != prev_palette.positive
129 	|| sm_palette.colors != prev_palette.colors) {
130 	/* print the message only if colors have changed */
131 	if (interactive)
132 	    fprintf(stderr, "smooth palette in %s: using %i of %i available color positions\n",
133 	    		term->name, sm_palette.colors, i);
134     }
135 
136     prev_palette = sm_palette;
137 
138     if (sm_palette.color != NULL) {
139 	free(sm_palette.color);
140 	sm_palette.color = NULL;
141     }
142     sm_palette.color = gp_alloc( sm_palette.colors * sizeof(rgb_color),
143 				 "pm3d palette color");
144 
145     /*  fill sm_palette.color[]  */
146     for (i = 0; i < sm_palette.colors; i++) {
147 	gray = (double) i / (sm_palette.colors - 1);	/* rescale to [0;1] */
148 	rgb1_from_gray( gray, &(sm_palette.color[i]) );
149     }
150 
151     /* let the terminal make the palette from the supplied RGB triplets */
152     term->make_palette(&sm_palette);
153 
154     return 0;
155 }
156 
157 /*
158  * Force a mismatch between the current palette and whatever is sent next,
159  * so that the new one will always be loaded
160  */
161 void
invalidate_palette()162 invalidate_palette()
163 {
164     prev_palette.colors = -1;
165 }
166 
167 /*
168    Set the colour on the terminal
169    Each terminal takes care of remembering the current colour,
170    so there is not much to do here.
171    FIXME: NaN could alternatively map to LT_NODRAW or TC_RGB full transparency
172  */
173 void
set_color(double gray)174 set_color(double gray)
175 {
176     t_colorspec color;
177     color.value = gray;
178     color.lt = LT_BACKGROUND;
179     color.type = (isnan(gray)) ? TC_LT : TC_FRAC;
180     term->set_color(&color);
181 }
182 
183 void
set_rgbcolor_var(unsigned int rgbvalue)184 set_rgbcolor_var(unsigned int rgbvalue)
185 {
186     t_colorspec color;
187     color.type = TC_RGB;
188     *(unsigned int *)(&color.lt) = rgbvalue;
189     color.value = -1;	/* -1 flags that this came from "rgb variable" */
190     apply_pm3dcolor(&color);
191 }
192 
193 void
set_rgbcolor_const(unsigned int rgbvalue)194 set_rgbcolor_const(unsigned int rgbvalue)
195 {
196     t_colorspec color;
197     color.type = TC_RGB;
198     *(unsigned int *)(&color.lt) = rgbvalue;
199     color.value = 0;	/* 0 flags that this is a constant color */
200     apply_pm3dcolor(&color);
201 }
202 
203 void
ifilled_quadrangle(gpiPoint * icorners)204 ifilled_quadrangle(gpiPoint* icorners)
205 {
206     if (default_fillstyle.fillstyle == FS_EMPTY)
207 	icorners->style = FS_OPAQUE;
208     else
209 	icorners->style = style_from_fill(&default_fillstyle);
210     term->filled_polygon(4, icorners);
211 
212     if (pm3d.border.l_type != LT_NODRAW) {
213 	int i;
214 
215 	/* LT_DEFAULT means draw border in current color */
216 	/* FIXME: currently there is no obvious way to set LT_DEFAULT  */
217 	if (pm3d.border.l_type != LT_DEFAULT) {
218 	    /* It should be sufficient to set only the color, but for some */
219 	    /* reason this causes the svg terminal to lose the fill type.  */
220 	    term_apply_lp_properties(&pm3d_border_lp);
221 	}
222 
223 	term->move(icorners[0].x, icorners[0].y);
224 	for (i = 3; i >= 0; i--) {
225 	    term->vector(icorners[i].x, icorners[i].y);
226 	}
227     }
228 }
229 
230 
231 /* The routine above for 4 points explicitly.
232  * This is the only routine which supportes extended
233  * color specs currently.
234  */
235 #ifdef EXTENDED_COLOR_SPECS
236 void
filled_quadrangle(gpdPoint * corners,gpiPoint * icorners)237 filled_quadrangle(gpdPoint * corners, gpiPoint * icorners)
238 #else
239 void
240 filled_quadrangle(gpdPoint * corners)
241 #endif
242 {
243     int i;
244     double x, y;
245 #ifndef EXTENDED_COLOR_SPECS
246     gpiPoint icorners[4];
247 #endif
248     for (i = 0; i < 4; i++) {
249 	map3d_xy_double(corners[i].x, corners[i].y, corners[i].z, &x, &y);
250 	icorners[i].x = x;
251 	icorners[i].y = y;
252     }
253 
254     ifilled_quadrangle(icorners);
255 }
256 
257 #ifdef PM3D_CONTOURS
258 /*
259    Makes mapping from real 3D coordinates, passed as coords array,
260    to 2D terminal coordinates, then draws filled polygon
261  */
262 void
filled_polygon_common(int points,struct coordinate GPHUGE * coords,TBOOLEAN fixed,double z)263 filled_polygon_common(int points, struct coordinate GPHUGE * coords, TBOOLEAN fixed, double z)
264 {
265     int i;
266     double x, y;
267     gpiPoint *icorners;
268     icorners = gp_alloc(points * sizeof(gpiPoint), "filled_polygon3d corners");
269     for (i = 0; i < points; i++) {
270 	if (fixed)
271 	    z = coords[i].z;
272 	map3d_xy_double(coords[i].x, coords[i].y, z, &x, &y);
273 	icorners[i].x = x;
274 	icorners[i].y = y;
275     }
276 #ifdef EXTENDED_COLOR_SPECS
277     if ((term->flags & TERM_EXTENDED_COLOR)) {
278 	icorners[0].spec.gray = -1;	/* force solid color */
279     }
280 #endif
281     if (default_fillstyle.fillstyle == FS_EMPTY)
282 	icorners->style = FS_OPAQUE;
283     else
284 	icorners->style = style_from_fill(&default_fillstyle);
285     term->filled_polygon(points, icorners);
286     free(icorners);
287 }
288 
289 void
filled_polygon_3dcoords(int points,struct coordinate GPHUGE * coords)290 filled_polygon_3dcoords(int points, struct coordinate GPHUGE * coords)
291 {
292     filled_polygon_common(points, coords, FALSE, 0.0);
293 }
294 
295 /*
296    Makes mapping from real 3D coordinates, passed as coords array, but at z coordinate
297    fixed (base_z, for instance) to 2D terminal coordinates, then draws filled polygon
298  */
299 void
filled_polygon_3dcoords_zfixed(int points,struct coordinate GPHUGE * coords,double z)300 filled_polygon_3dcoords_zfixed(int points, struct coordinate GPHUGE * coords, double z)
301 {
302     filled_polygon_common(points, coords, TRUE, z);
303 }
304 
305 #endif /* PM3D_CONTOURS */
306 
307 
308 /*
309    Draw colour smooth box
310 
311    Firstly two helper routines for plotting inside of the box
312    for postscript and for other terminals, finally the main routine
313  */
314 
315 
316 /* plot the colour smooth box for from terminal's integer coordinates
317    This routine is for postscript files --- actually, it writes a small
318    PS routine.
319  */
320 static void
draw_inside_color_smooth_box_postscript()321 draw_inside_color_smooth_box_postscript()
322 {
323     int scale_x = (color_box.bounds.xright - color_box.bounds.xleft), scale_y = (color_box.bounds.ytop - color_box.bounds.ybot);
324     fputs("stroke gsave\t%% draw gray scale smooth box\n"
325 	  "maxcolors 0 gt {/imax maxcolors def} {/imax 1024 def} ifelse\n", gppsfile);
326 
327     /* nb. of discrete steps (counted in the loop) */
328     fprintf(gppsfile, "%i %i translate %i %i scale 0 setlinewidth\n", color_box.bounds.xleft, color_box.bounds.ybot, scale_x, scale_y);
329     /* define left bottom corner and scale of the box so that all coordinates
330        of the box are from [0,0] up to [1,1]. Further, this normalization
331        makes it possible to pass y from [0,1] as parameter to setgray */
332     fprintf(gppsfile, "/ystep 1 imax div def /y0 0 def /ii 0 def\n");
333     /* local variables; y-step, current y position and counter ii;  */
334     if (sm_palette.positive == SMPAL_NEGATIVE)	/* inverted gray for negative figure */
335 	fputs("{ 0.99999 y0 sub g ", gppsfile); /* 1 > x > 1-1.0/1024 */
336     else
337 	fputs("{ y0 g ", gppsfile);
338     if (color_box.rotation == 'v')
339 	fputs("0 y0 N 1 0 V 0 ystep V -1 0 f\n", gppsfile);
340     else
341 	fputs("y0 0 N 0 1 V ystep 0 V 0 -1 f\n", gppsfile);
342     fputs("/y0 y0 ystep add def /ii ii 1 add def\n"
343 	  "ii imax ge {exit} if } loop\n"
344 	  "grestore 0 setgray\n", gppsfile);
345 }
346 
347 
348 
349 /* plot a colour smooth box bounded by the terminal's integer coordinates
350    [x_from,y_from] to [x_to,y_to].
351    This routine is for non-postscript files, as it does an explicit loop
352    over all thin rectangles
353  */
354 static void
draw_inside_color_smooth_box_bitmap()355 draw_inside_color_smooth_box_bitmap()
356 {
357     int steps = 128; /* I think that nobody can distinguish more colours drawn in the palette */
358     int i, j, xy, xy2, xy_from, xy_to;
359     int jmin = 0;
360     double xy_step, gray, range;
361     gpiPoint corners[4];
362 
363     if (color_box.rotation == 'v') {
364 	corners[0].x = corners[3].x = color_box.bounds.xleft;
365 	corners[1].x = corners[2].x = color_box.bounds.xright;
366 	xy_from = color_box.bounds.ybot;
367 	xy_to = color_box.bounds.ytop;
368 	xy_step = (color_box.bounds.ytop - color_box.bounds.ybot) / (double)steps;
369     } else {
370 	corners[0].y = corners[1].y = color_box.bounds.ybot;
371 	corners[2].y = corners[3].y = color_box.bounds.ytop;
372 	xy_from = color_box.bounds.xleft;
373 	xy_to = color_box.bounds.xright;
374 	xy_step = (color_box.bounds.xright - color_box.bounds.xleft) / (double)steps;
375     }
376     range = (xy_to - xy_from);
377 
378     for (i = 0, xy2 = xy_from; i < steps; i++) {
379 
380 	/* Start from one pixel beyond the previous box */
381 	xy = xy2;
382 	xy2 = xy_from + (int) (xy_step * (i + 1));
383 
384 	/* Set the colour for the next range increment */
385 	/* FIXME - The "1 +" seems wrong, yet it improves the placement in gd */
386 	gray = (double)(1 + xy - xy_from) / range;
387 	if (sm_palette.positive == SMPAL_NEGATIVE)
388 	    gray = 1 - gray;
389 	set_color(gray);
390 
391 	/* If this is a defined palette, make sure that the range increment */
392 	/* does not straddle a palette segment boundary. If it does, split  */
393 	/* it into two parts.                                               */
394 	if (sm_palette.colorMode == SMPAL_COLOR_MODE_GRADIENT)
395 	    for (j=jmin; j<sm_palette.gradient_num; j++) {
396 		int boundary = xy_from + (int)(sm_palette.gradient[j].pos * range);
397 		if (xy >= boundary) {
398 		    jmin = j;
399 		} else {
400 		    if (xy2 > boundary) {
401 			xy2 = boundary;
402 			i--;
403 			break;
404 		    }
405 		}
406 		if (xy2 < boundary)
407 		    break;
408 	    }
409 
410 	if (color_box.rotation == 'v') {
411 	    corners[0].y = corners[1].y = xy;
412 	    corners[2].y = corners[3].y = GPMIN(xy_to,xy2+1);
413 	} else {
414 	    corners[0].x = corners[3].x = xy;
415 	    corners[1].x = corners[2].x = GPMIN(xy_to,xy2+1);
416 	}
417 #ifdef EXTENDED_COLOR_SPECS
418 	if ((term->flags & TERM_EXTENDED_COLOR))
419 	    corners[0].spec.gray = -1;	/* force solid color */
420 #endif
421 	/* print the rectangle with the given colour */
422 	if (default_fillstyle.fillstyle == FS_EMPTY)
423 	    corners->style = FS_OPAQUE;
424 	else
425 	    corners->style = style_from_fill(&default_fillstyle);
426 	term->filled_polygon(4, corners);
427     }
428 }
429 
430 static void
cbtick_callback(struct axis * this_axis,double place,char * text,int ticlevel,struct lp_style_type grid,struct ticmark * userlabels)431 cbtick_callback(
432     struct axis *this_axis,
433     double place,
434     char *text,
435     int ticlevel,
436     struct lp_style_type grid, /* linetype or -2 for no grid */
437     struct ticmark *userlabels)
438 {
439     int len = tic_scale(ticlevel, this_axis)
440 	* (this_axis->tic_in ? -1 : 1) * (term->h_tic);
441     unsigned int x1, y1, x2, y2;
442     double cb_place;
443 
444     /* position of tic as a fraction of the full palette range */
445 #ifdef NONLINEAR_AXES
446     if (this_axis->linked_to_primary) {
447 	AXIS * primary = this_axis->linked_to_primary;
448 	place = eval_link_function(primary, place);
449 	cb_place = (place - primary->min) / (primary->max - primary->min);
450     } else
451 #endif
452     cb_place = (place - this_axis->min) / (this_axis->max - this_axis->min);
453 
454     /* calculate tic position */
455     if (color_box.rotation == 'h') {
456 	x1 = x2 = color_box.bounds.xleft + cb_place * (color_box.bounds.xright - color_box.bounds.xleft);
457 	y1 = color_box.bounds.ybot;
458 	y2 = color_box.bounds.ybot - len;
459     } else {
460 	x1 = color_box.bounds.xright;
461 	x2 = color_box.bounds.xright + len;
462 	y1 = y2 = color_box.bounds.ybot + cb_place * (color_box.bounds.ytop - color_box.bounds.ybot);
463     }
464 
465     /* draw grid line */
466     if (grid.l_type > LT_NODRAW) {
467 	term_apply_lp_properties(&grid);	/* grid linetype */
468 	if (color_box.rotation == 'h') {
469 	    (*term->move) (x1, color_box.bounds.ybot);
470 	    (*term->vector) (x1, color_box.bounds.ytop);
471 	} else {
472 	    (*term->move) (color_box.bounds.xleft, y1);
473 	    (*term->vector) (color_box.bounds.xright, y1);
474 	}
475 	term_apply_lp_properties(&border_lp);	/* border linetype */
476     }
477 
478     /* draw tic */
479     (*term->move) (x1, y1);
480     (*term->vector) (x2, y2);
481 
482     /* draw label */
483     if (text) {
484 	int just;
485 	int offsetx, offsety;
486 
487 	/* Skip label if we've already written a user-specified one here */
488 #	define MINIMUM_SEPARATION 0.001
489 	while (userlabels) {
490 	    if (fabs((place - userlabels->position) / (CB_AXIS.max - CB_AXIS.min))
491 		<= MINIMUM_SEPARATION) {
492 		text = NULL;
493 		break;
494 	    }
495 	    userlabels = userlabels->next;
496 	}
497 #	undef MINIMUM_SEPARATION
498 
499 	/* get offset */
500 	map3d_position_r(&(this_axis->ticdef.offset),
501 			 &offsetx, &offsety, "cbtics");
502 	/* User-specified different color for the tics text */
503 	if (this_axis->ticdef.textcolor.type != TC_DEFAULT)
504 	    apply_pm3dcolor(&(this_axis->ticdef.textcolor));
505 	if (color_box.rotation == 'h') {
506 	    int y3 = color_box.bounds.ybot - (term->v_char);
507 	    int hrotate = 0;
508 
509 	    if (this_axis->tic_rotate
510 		&& (*term->text_angle)(this_axis->tic_rotate))
511 		    hrotate = this_axis->tic_rotate;
512 	    if (len > 0) y3 -= len; /* add outer tics len */
513 	    if (y3<0) y3 = 0;
514 	    just = hrotate ? LEFT : CENTRE;
515 	    if (this_axis->manual_justify)
516 		just = this_axis->tic_pos;
517 	    write_multiline(x2+offsetx, y3+offsety, text,
518 			    just, JUST_CENTRE, hrotate,
519 			    this_axis->ticdef.font);
520 	    if (hrotate)
521 		(*term->text_angle)(0);
522 	} else {
523 	    unsigned int x3 = color_box.bounds.xright + (term->h_char);
524 	    if (len > 0) x3 += len; /* add outer tics len */
525 	    just = LEFT;
526 	    if (this_axis->manual_justify)
527 		just = this_axis->tic_pos;
528 	    write_multiline(x3+offsetx, y2+offsety, text,
529 			    just, JUST_CENTRE, 0.0,
530 			    this_axis->ticdef.font);
531 	}
532 	term_apply_lp_properties(&border_lp);	/* border linetype */
533     }
534 
535     /* draw tic on the mirror side */
536     if (this_axis->ticmode & TICS_MIRROR) {
537 	if (color_box.rotation == 'h') {
538 	    y1 = color_box.bounds.ytop;
539 	    y2 = color_box.bounds.ytop + len;
540 	} else {
541 	    x1 = color_box.bounds.xleft;
542 	    x2 = color_box.bounds.xleft - len;
543 	}
544 	(*term->move) (x1, y1);
545 	(*term->vector) (x2, y2);
546     }
547 }
548 
549 /*
550    Finally the main colour smooth box drawing routine
551  */
552 void
draw_color_smooth_box(int plot_mode)553 draw_color_smooth_box(int plot_mode)
554 {
555     if (color_box.where == SMCOLOR_BOX_NO)
556 	return;
557     if (!term->filled_polygon)
558 	return;
559 
560     /*
561        firstly, choose some good position of the color box
562 
563        user's position like that (?):
564        else {
565        x_from = color_box.xlow;
566        x_to   = color_box.xhigh;
567        }
568      */
569     if (color_box.where == SMCOLOR_BOX_USER) {
570 	if (!is_3d_plot) {
571 	    double xtemp, ytemp;
572 	    map_position(&color_box.origin, &color_box.bounds.xleft, &color_box.bounds.ybot, "cbox");
573 	    map_position_r(&color_box.size, &xtemp, &ytemp, "cbox");
574 	    color_box.bounds.xright = xtemp;
575 	    color_box.bounds.ytop = ytemp;
576 	} else if (splot_map && is_3d_plot) {
577 	    /* In map view mode we allow any coordinate system for placement */
578 	    double xtemp, ytemp;
579 	    map3d_position_double(&color_box.origin, &xtemp, &ytemp, "cbox");
580 	    color_box.bounds.xleft = xtemp;
581 	    color_box.bounds.ybot = ytemp;
582 	    map3d_position_r(&color_box.size, &color_box.bounds.xright, &color_box.bounds.ytop, "cbox");
583 	} else {
584 	    /* But in full 3D mode we only allow screen coordinates */
585 	    color_box.bounds.xleft = color_box.origin.x * (term->xmax) + 0.5;
586 	    color_box.bounds.ybot = color_box.origin.y * (term->ymax) + 0.5;
587 	    color_box.bounds.xright = color_box.size.x * (term->xmax-1) + 0.5;
588 	    color_box.bounds.ytop = color_box.size.y * (term->ymax-1) + 0.5;
589 	}
590 	color_box.bounds.xright += color_box.bounds.xleft;
591 	color_box.bounds.ytop += color_box.bounds.ybot;
592 
593     } else { /* color_box.where == SMCOLOR_BOX_DEFAULT */
594 	if (plot_mode == MODE_SPLOT && !splot_map) {
595 	    /* general 3D plot */
596 	    color_box.bounds.xleft = xmiddle + 0.709 * xscaler;
597 	    color_box.bounds.xright   = xmiddle + 0.778 * xscaler;
598 	    color_box.bounds.ybot = ymiddle - 0.147 * yscaler;
599 	    color_box.bounds.ytop   = ymiddle + 0.497 * yscaler;
600 	} else {
601 	    /* 2D plot (including splot map) */
602 	    struct position default_origin = {graph,graph,graph, 1.025, 0, 0};
603 	    struct position default_size = {graph,graph,graph, 0.05, 1.0, 0};
604 	    double xtemp, ytemp;
605 	    map_position(&default_origin, &color_box.bounds.xleft, &color_box.bounds.ybot, "cbox");
606 	    color_box.bounds.xleft += color_box.xoffset;
607 	    map_position_r(&default_size, &xtemp, &ytemp, "cbox");
608 	    color_box.bounds.xright = xtemp + color_box.bounds.xleft;
609 	    color_box.bounds.ytop = ytemp + color_box.bounds.ybot;
610 	}
611 
612 	/* now corrections for outer tics */
613 	if (color_box.rotation == 'v') {
614 	    int cblen = (CB_AXIS.tic_in ? -1 : 1) * CB_AXIS.ticscale *
615 		(term->h_tic); /* positive for outer tics */
616 	    int ylen = (Y_AXIS.tic_in ? -1 : 1) * Y_AXIS.ticscale *
617 		(term->h_tic); /* positive for outer tics */
618 	    if ((cblen > 0) && (CB_AXIS.ticmode & TICS_MIRROR)) {
619 		color_box.bounds.xleft += cblen;
620 		color_box.bounds.xright += cblen;
621 	    }
622 	    if ((ylen > 0) &&
623 		(axis_array[FIRST_Y_AXIS].ticmode & TICS_MIRROR)) {
624 		color_box.bounds.xleft += ylen;
625 		color_box.bounds.xright += ylen;
626 	    }
627 	}
628     }
629 
630     if (color_box.bounds.ybot > color_box.bounds.ytop) {
631 	double tmp = color_box.bounds.ytop;
632 	color_box.bounds.ytop = color_box.bounds.ybot;
633 	color_box.bounds.ybot = tmp;
634     }
635     if (color_box.invert && color_box.rotation == 'v') {
636 	double tmp = color_box.bounds.ytop;
637 	color_box.bounds.ytop = color_box.bounds.ybot;
638 	color_box.bounds.ybot = tmp;
639     }
640 
641     term->layer(TERM_LAYER_BEGIN_COLORBOX);
642 
643     /* The PostScript terminal has an Optimized version */
644     if ((term->flags & TERM_IS_POSTSCRIPT) != 0)
645 	draw_inside_color_smooth_box_postscript();
646     else
647 	draw_inside_color_smooth_box_bitmap();
648 
649     term->layer(TERM_LAYER_END_COLORBOX);
650 
651     if (color_box.border) {
652 	/* now make boundary around the colour box */
653 	if (color_box.border_lt_tag >= 0) {
654 	    /* user specified line type */
655 	    struct lp_style_type lp = border_lp;
656 	    lp_use_properties(&lp, color_box.border_lt_tag);
657 	    term_apply_lp_properties(&lp);
658 	} else {
659 	    /* black solid colour should be chosen, so it's border linetype */
660 	    term_apply_lp_properties(&border_lp);
661 	}
662 	newpath();
663 	(term->move) (color_box.bounds.xleft, color_box.bounds.ybot);
664 	(term->vector) (color_box.bounds.xright, color_box.bounds.ybot);
665 	(term->vector) (color_box.bounds.xright, color_box.bounds.ytop);
666 	(term->vector) (color_box.bounds.xleft, color_box.bounds.ytop);
667 	(term->vector) (color_box.bounds.xleft, color_box.bounds.ybot);
668 	closepath();
669 
670 	/* Set line properties to some value, this also draws lines in postscript terminals. */
671 	    term_apply_lp_properties(&border_lp);
672 	}
673 
674     /* draw tics */
675     if (axis_array[COLOR_AXIS].ticmode) {
676 	term_apply_lp_properties(&border_lp); /* border linetype */
677 	gen_tics(&axis_array[COLOR_AXIS], cbtick_callback );
678     }
679 
680     /* write the colour box label */
681     if (CB_AXIS.label.text) {
682 	int x, y;
683 	int len;
684 	int save_rotation = CB_AXIS.label.rotate;
685 	apply_pm3dcolor(&(CB_AXIS.label.textcolor));
686 	if (color_box.rotation == 'h') {
687 	    len = CB_AXIS.ticscale * (CB_AXIS.tic_in ? 1 : -1) * (term->v_tic);
688 
689 	    x = (color_box.bounds.xleft + color_box.bounds.xright) / 2;
690 	    y = color_box.bounds.ybot - 2.7 * term->v_char;
691 
692 	    if (len < 0) y += len;
693 	    if (CB_AXIS.label.rotate == TEXT_VERTICAL)
694 		CB_AXIS.label.rotate = 0;
695 	} else {
696 	    len = CB_AXIS.ticscale * (CB_AXIS.tic_in ? -1 : 1) * (term->h_tic);
697 	    /* calculate max length of cb-tics labels */
698 	    widest_tic_strlen = 0;
699 	    if (CB_AXIS.ticmode & TICS_ON_BORDER) /* Recalculate widest_tic_strlen */
700 		gen_tics(&axis_array[COLOR_AXIS], widest_tic_callback);
701 	    x = color_box.bounds.xright + (widest_tic_strlen + 1.5) * term->h_char;
702 	    if (len > 0) x += len;
703 	    y = (color_box.bounds.ybot + color_box.bounds.ytop) / 2;
704 	}
705 	if (x<0) x = 0;
706 	if (y<0) y = 0;
707 	write_label(x, y, &(CB_AXIS.label));
708 	reset_textcolor(&(CB_AXIS.label.textcolor));
709 	CB_AXIS.label.rotate = save_rotation;
710     }
711 
712 }
713 
714 /*
715  * User-callable builtin color conversion
716  */
717 void
f_hsv2rgb(union argument * arg)718 f_hsv2rgb(union argument *arg)
719 {
720     struct value h, s, v, result;
721     rgb_color color = {0., 0., 0.};
722 
723     (void) arg;
724     (void) pop(&v);
725     (void) pop(&s);
726     (void) pop(&h);
727 
728     if (h.type == INTGR)
729 	color.r = h.v.int_val;
730     else if (h.type == CMPLX)
731 	color.r = h.v.cmplx_val.real;
732     if (s.type == INTGR)
733 	color.g = s.v.int_val;
734     else if (s.type == CMPLX)
735 	color.g = s.v.cmplx_val.real;
736     if (v.type == INTGR)
737 	color.b = v.v.int_val;
738     else if (v.type == CMPLX)
739 	color.b = v.v.cmplx_val.real;
740 
741     if (color.r < 0)
742 	color.r = 0;
743     if (color.g < 0)
744 	color.g = 0;
745     if (color.b < 0)
746 	color.b = 0;
747     if (color.r > 1.)
748 	color.r = 1.;
749     if (color.g > 1.)
750 	color.g = 1.;
751     if (color.b > 1.)
752 	color.b = 1.;
753 
754     (void) Ginteger(&result, hsv2rgb(&color));
755     push(&result);
756 }
757