/*--------------------------------------------------------------------
 *	$Id: psscale.c,v 1.4.4.3 2002/02/27 17:41:10 pwessel Exp $
 *
 *	Copyright (c) 1991-2002 by P. Wessel and W. H. F. Smith
 *	See COPYING file for copying and redistribution conditions.
 *
 *	This program is free software; you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License as published by
 *	the Free Software Foundation; version 2 of the License.
 *
 *	This program is distributed in the hope that it will be useful,
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *	GNU General Public License for more details.
 *
 *	Contact info: gmt.soest.hawaii.edu
 *--------------------------------------------------------------------*/
/*
 * psscale draws a grayscale or colorscale either vertically or
 * horizontally.  psscale will interpolate colors if the lower
 * and upper rgb values for an interval are different.  The
 * resolution for this interpolation can be set on the command line.
 * If the scale is to be used with illumninated 2-D and 3-D plots
 * then options for intensities are available.
 *
 * Author:	Paul Wessel
 * Created:	10-MAY-1991
 * Modified:	01-AUG-1998
 *		3.3   13-APR-1999. PW: Fixed bad -X -Y behavior
 *		3.3.2 15-SEP-1999. PW: Fixed bad loop for -Z
 *		3.3.4 21-JAN-2000. PW: Now paint rectangles for discrete cpt files
 *		      08-FEB-2000. PW: Also understand patterns for discrete cpt files
 * Version:	3.4.1
 *
 */

#include "gmt.h"

int bit_dpi = 300;
BOOLEAN B_set = FALSE;

void GMT_draw_colorbar (double length, double width, double *z_width, BOOLEAN horizontal, BOOLEAN intens, double max_intens, BOOLEAN skip_lines, BOOLEAN extend, double e_length, BOOLEAN monochrome);
void fix_format (char *unit, char *format);

main(int argc, char **argv)
{
	int i, n;
	
	BOOLEAN horizontal = FALSE, equi = FALSE, intens = FALSE, error = FALSE;
	BOOLEAN skip_lines = FALSE, extend = FALSE, must_shift_back = FALSE, monochrome = FALSE;
	
	double xpos, ypos, length, width, max_intens = 1.0, dz;
	double *z_width, e_length = 0.0, x_origin, y_origin;
	
	char flag, *cpt_file = NULL, *z_file = NULL, line[BUFSIZ];
	char txt_a[32], txt_b[32], txt_c[32], txt_d[32], text[128];
	
	FILE *fp = NULL;
	
	argc = GMT_begin (argc, argv);

	length = width = xpos = ypos = x_origin = y_origin = 0.0;
	
	/* Check and interpret the command line arguments */
	
	for (i = 1; i < argc; i++) {
		if (argv[i][0] == '-') {
			switch(argv[i][1]) {
		
				/* Common parameters */

				case 'B':
					if (! (argv[i][2] == ':' || argv[i][2] == '/')) B_set = TRUE;
				case 'K':
				case 'O':
				case 'P':
				case 'U':
				case 'V':
				case 'X':
				case 'x':
				case 'Y':
				case 'y':
				case 'c':
				case '\0':
					error += GMT_get_common_args (argv[i], 0, 0, 0, 0);
					break;
				
				/* Supplemental parameters */
			
				case 'C':
					cpt_file = &argv[i][2];
					break;
				case 'D':
					n = strlen(argv[i]) - 1;
					flag = argv[i][n];
					if (flag == 'h' || flag == 'H') {
						horizontal = TRUE;
						argv[i][n] = 0;
					}
					sscanf (&argv[i][2], "%[^/]/%[^/]/%[^/]/%s", txt_a, txt_b, txt_c, txt_d);
					xpos   = GMT_convert_units (txt_a, GMT_INCH);
					ypos   = GMT_convert_units (txt_b, GMT_INCH);
					length = GMT_convert_units (txt_c, GMT_INCH);
					width  = GMT_convert_units (txt_d, GMT_INCH);
					break;
				case 'E':
					if (argv[i][2]) e_length = GMT_convert_units (&argv[i][2], GMT_INCH);
					extend = TRUE;
					break;
				case 'I':
					intens = TRUE;
					if (argv[i][2]) max_intens = atof (&argv[i][2]);
					break;
				case 'M':
					monochrome = TRUE;
					break;
				case 'N':
					if (argv[i][2]) bit_dpi = atoi (&argv[i][2]);
					break;
				case 'L':
					equi = TRUE;
					break;
				case 'S':
					skip_lines = TRUE;
					break;
				case 'Z':
					z_file = &argv[i][2];
					break;
					
				/* Options not recognized */
						
				default:
					error = TRUE;
					GMT_default_error (argv[i][1]);
					break;
			}
		}
	}
	
	if (argc == 1 || GMT_quick) {
		fprintf (stderr, "%s %s - To create grayscale or colorscale for maps\n\n", GMT_program, GMT_VERSION);
		fprintf(stderr, "usage: psscale -D<xpos/ypos/length/width[h]> [-C<cpt_file>] [-E[<length>]] [-B<tickinfo>] [-I[<intens>]\n");
		fprintf(stderr, "\t[-K] [-L] [-M] [-N<dpi>] [-O] [-P] [-S] [-U] [-V] [-X<x_shift>] [-Y<y_shift>] [-Z<zfile>] [-c<ncopies>]\n\n");
		if (GMT_quick) exit (EXIT_FAILURE);
		
		fprintf (stderr, "\t-D set mid-point position and length/width for scale.\n");
		fprintf (stderr, "\t   Give negative length to reverse the scalebar\n");
		fprintf (stderr, "\t   Append h for horizontal scale\n");
		fprintf (stderr, "\n\tOPTIONS:\n");
		fprintf (stderr, "\t-B Set scale anotation interval and label. Use y-label to set unit label\n");
		fprintf (stderr, "\t   If no anotation interval is set it is taken from the cpt file\n");
		fprintf (stderr, "\t-C Color palette file. If not set, stdin is read.\n");
		fprintf (stderr, "\t   By default all color changes are anotated (but see -B).  To use a subset,\n");
		fprintf (stderr, "\t   add an extra column to the cpt-file with a L, U, or B\n");
		fprintf (stderr, "\t   to anotate Lower, Upper, or Both color segment boundaries\n");
		fprintf (stderr, "\t-E add sidebar triangles for back- and forground colors.\n");
		fprintf (stderr, "\t   Optionally, append triangle height [Default scales with barwidth]\n");
		fprintf (stderr, "\t-I add illumination for +- <max_intens> [1.0]\n");
		GMT_explain_option ('K');
		fprintf (stderr, "\t-L For equal-sized color rectangles\n");
		fprintf (stderr, "\t-M force monochrome colorbar using YIQ transformation\n");
		fprintf (stderr, "\t-N effective dots-per-inch for color scale [300]\n");
		GMT_explain_option ('O');
		GMT_explain_option ('P');
		fprintf (stderr, "\t-S Skip drawing color boundary lines on color scale [Default draws lines]\n");
		GMT_explain_option ('U');
		GMT_explain_option ('V');
		GMT_explain_option ('X');
		fprintf (stderr, "\t-Z give colorbar-width (in %s) per color entry\n", GMT_unit_names[gmtdefs.measure_unit]);
		fprintf (stderr, "\t   By default, width of entry is scaled to color range\n");
		fprintf (stderr, "\t   i.e., z = 0-100 gives twice the width as z = 100-150.\n");
		GMT_explain_option ('c');
		GMT_explain_option ('.');
		exit (EXIT_FAILURE);
	}

	/* Check that the options selected are mutually consistant */
	
	if (z_project.view_azimuth > 360.0 || z_project.view_elevation <= 0.0 || z_project.view_elevation > 90.0) {
		fprintf (stderr, "%s: GMT SYNTAX ERROR -E option:  Enter azimuth in 0-360 range, elevation in 0-90 range\n", GMT_program);
		error++;
	}
	if (fabs (length) < SMALL ) {
		fprintf (stderr, "%s: GMT SYNTAX ERROR -D option: scale length must be nonzero\n", GMT_program);
		error++;
	}
	if (width <= 0.0) {
		fprintf (stderr, "%s: GMT SYNTAX ERROR -D option: scale width must be positive\n", GMT_program);
		error++;
	}
	if (bit_dpi < 1) {
		fprintf (stderr, "%s: GMT SYNTAX ERROR -N option: dpi must be > 0\n", GMT_program);
		error++;
	}
	      
	if (error) exit (EXIT_FAILURE);

	GMT_put_history (argc, argv);	/* Update .gmtcommands */

	if (gmtdefs.verbose) {
		if (cpt_file)
			fprintf (stderr, "%s: Reading CPT file %s.", GMT_program, cpt_file);
		else
			fprintf (stderr, "%s: Reading standard input.", GMT_program);
	}

	GMT_read_cpt (cpt_file);
	if (gmtdefs.verbose) fprintf (stderr, "  CPT range from %lg to %lg\n",
		GMT_lut[0].z_low, GMT_lut[GMT_n_colors-1].z_high);

	if (extend && e_length == 0.0) e_length = width * 0.5;

	z_width = (double *) GMT_memory (VNULL, (size_t)GMT_n_colors, sizeof (double), GMT_program);
	
	if (z_file && (fp = fopen (z_file, "r")) == NULL) {
		fprintf (stderr, "%s: Unable to open file %s\n", GMT_program, z_file);
		exit (EXIT_FAILURE);
	}
	else if (z_file) {	/* Opened successfully, now read */
		i = 0;
		while (fgets (line, BUFSIZ, fp)) {
			if (i == GMT_n_colors) {
				fprintf (stderr, "%s: -Z file %s has more slices than -C file %s!\n", GMT_program, z_file, cpt_file);
				exit (EXIT_FAILURE);
			}
			if (line[0] == '#' || line[0] == '\n') continue;	/* Skip comments and blank lines */
			n = sscanf (line, "%lf", &z_width[i++]);
			if (n != 1) {
				fprintf (stderr, "%s: Error reading file %s near line %d\n", GMT_program, z_file, i);
				exit (EXIT_FAILURE);
			}
		}
		fclose (fp);
		if (i < GMT_n_colors) {
			fprintf (stderr, "%s: -Z file %s has fewer slices than -C file %s!\n", GMT_program, z_file, cpt_file);
			exit (EXIT_FAILURE);
		}
	}
	else if (equi) {
		dz = fabs (length) / GMT_n_colors;
		for (i = 0; i < GMT_n_colors; i++) z_width[i] = dz;
	}
	else {
		for (i = 0; i < GMT_n_colors; i++) z_width[i] = fabs (length) * (GMT_lut[i].z_high - GMT_lut[i].z_low) / (GMT_lut[GMT_n_colors-1].z_high - GMT_lut[0].z_low);
	}

/*-----------------start of kludge-------------------------------------------*/

	/* Because psscale uses -D to position things we need to make some
	 * changes so that BoundingBox and others are set ~correctly */

	sprintf (text, "X%lgi/%lgi", length, width);
	GMT_map_getproject (text);	/* Fake linear projection */
	GMT_map_setup (GMT_lut[0].z_low, GMT_lut[GMT_n_colors-1].z_high, 0.0, width);

	/* We must do any origin translation manually in psscale */

	if (! (gmtdefs.x_origin == 0.0 && gmtdefs.y_origin == 0.0)) {	/* Must shift */
		must_shift_back = (gmtdefs.page_orientation & 8);	/* TRUE if absolute */
		x_origin = gmtdefs.x_origin;
		y_origin = gmtdefs.y_origin;
	}

	if (horizontal) {
		gmtdefs.x_origin = xpos - 0.5 * fabs (length);
		gmtdefs.y_origin = ypos - width;
		frame_info.side[1] = frame_info.side[2] = frame_info.side[3] = 0;
	}
	else {
		gmtdefs.x_origin = xpos;
		gmtdefs.y_origin = ypos - 0.5 * fabs (length);
		frame_info.side[0] = frame_info.side[2] = frame_info.side[3] = 0;
		d_swap (z_project.xmin, z_project.ymin);
		d_swap (z_project.xmax, z_project.ymax);
	}
	if (frame_info.anot_int[0] == 0.0) frame_info.plot = TRUE;
	gmtdefs.page_orientation |= 8;	/* Ensure absolute coordinates */

/*-----------------end of kludge-------------------------------------------*/

	ps_plotinit (CNULL, gmtdefs.overlay, gmtdefs.page_orientation, gmtdefs.x_origin, gmtdefs.y_origin,
		gmtdefs.global_x_scale, gmtdefs.global_y_scale, gmtdefs.n_copies,
		gmtdefs.dpi, GMT_INCH, gmtdefs.paper_width, gmtdefs.page_rgb, GMT_epsinfo (argv[0]));
		
	GMT_echo_command (argc, argv);
	
	ps_transrotate (x_origin, y_origin, 0.0);

	ps_setpaint (gmtdefs.basemap_frame_rgb);
	
	if (gmtdefs.unix_time) {	/* Extra shift/unshift since psscale does things differently */
		ps_transrotate (-gmtdefs.x_origin, -gmtdefs.y_origin, 0.0);
		GMT_timestamp (argc, argv);
		ps_transrotate (gmtdefs.x_origin, gmtdefs.y_origin, 0.0);
	}
	
	GMT_draw_colorbar (length, width, z_width, horizontal, intens, max_intens, skip_lines, extend, e_length, monochrome);

	if (must_shift_back) ps_transrotate (-x_origin, -y_origin, 0.0);

	ps_plotend (gmtdefs.last_page);
	
	GMT_free ((void *)z_width);
	
	GMT_end (argc, argv);
}

void GMT_draw_colorbar (double length, double width, double *z_width, BOOLEAN horizontal, BOOLEAN intens, double max_intens, BOOLEAN skip_lines, BOOLEAN extend, double e_length, BOOLEAN monochrome)
{
	int i, ii, j, ndec = -1, dec, rgb[3], rrggbb[3];
	int nx, ny, nm, barmem, k;
	BOOLEAN reverse, all = TRUE, use_image;
	char format[64], text[64], test[64];
	unsigned char *bar;
	double off, anot_off, label_off, len, len2, size, x0, x1, dx, xx;
	double z, xleft, xright, inc_i, inc_j;
	double get_z (double x, double *width, int n);
	double xp[4], yp[4];
	struct GMT_FILL *f;
	
	ps_setfont (gmtdefs.anot_font);

	gmtdefs.anot_offset = fabs (gmtdefs.anot_offset);	/* No 'inside' anotations allowed in colorbar */

	/* Find max decimals needed */
	

	if (B_set) {
		ndec = GMT_get_format (frame_info.anot_int[0], frame_info.unit[0], format);
	}
	else {
		for (i = 0; i < GMT_n_colors; i++) {
			if (GMT_lut[i].anot & 1) {
				if ((dec = GMT_get_format (GMT_lut[i].z_low, CNULL, text)) > ndec) {
					strcpy (format, text);
					ndec = dec;
				}
			}
			if (GMT_lut[i].anot & 2) {
				if ((dec = GMT_get_format (GMT_lut[i].z_high, CNULL, text)) > ndec) {
					strcpy (format, text);
					ndec = dec;
				}
			}
			if (GMT_lut[i].anot) all = FALSE;
		}
	}
	
	if (ndec == 0) {	/* Not -B and no decimals are needed */
		strcpy (format, gmtdefs.d_format);
		fix_format (frame_info.unit[0], format);	/* Add units if needed */
	}

	len = gmtdefs.tick_length;	/* +ve means draw on the outside of bar */
	len2 = 0.5 * len;
	xleft = x0 = 0.0;

	reverse = (length < 0.0);
	length = fabs (length);
	xright = length;

	use_image = (GMT_continuous || intens);
	
	if (use_image) {	/* Make bitimage for colorbar using bit_dpi */
		nx = irint (length * bit_dpi);
		ny = irint (width * bit_dpi);
		nm = nx * ny;
		inc_i = 1.0 / bit_dpi;
		inc_j = (intens) ? 2.0 * max_intens / (ny - 1) : 0.0;
		barmem = (monochrome || GMT_gray) ? nm : 3 * nm;
		bar = (unsigned char *) GMT_memory (VNULL, (size_t)barmem, sizeof (char), GMT_program);
	
		/* Load bar image */
	
		for (i = 0; i < nx; i++) {
			z = get_z (i * inc_i, z_width, GMT_n_colors);
			GMT_get_rgb24 (z, rrggbb);
			ii = (reverse) ? nx - i - 1 : i;
			for (j = 0; j < ny; j++) {
				for (k = 0; k < 3; k++) rgb[k] = rrggbb[k];
				k = j * nx + ii;
				if (intens) GMT_illuminate (max_intens - j * inc_j, rgb);
				if (GMT_gray)	/* All gray, pick red */
					bar[k] =  (unsigned char) rgb[0];
				else if (monochrome)	/* Convert to gray using the YIQ transformation */
					bar[k] =  (unsigned char) YIQ (rgb);
				else {
					k *= 3;
					bar[k++] = (unsigned char) rgb[0];
					bar[k++] = (unsigned char) rgb[1];
					bar[k++] = (unsigned char) rgb[2];
				}
			}
		}
	}
	
	GMT_setpen (&gmtdefs.frame_pen);

	/* Current point (0,0) is now at lower left location of bar */

	if (horizontal) {
		if (use_image) {	/* Must plot as image */
			if (monochrome || GMT_gray)
				ps_image (0.0, 0.0, length, width, bar, nx, ny, 8);
			else 
				GMT_color_image (0.0, 0.0, length, width, bar, nx, ny);
		}
		else {			/* Plot as rectangles */
			x0 = x1 = 0.0;
			yp[0] = yp[1] = 0.0;	yp[2] = yp[3] = width;
			for (i = 0; i < GMT_n_colors; i++) {
				x1 += z_width[i];
				if ((f = GMT_lut[i].fill)) {	/* Using pattern fills */
					xp[0] = xp[3] = x0;	xp[1] = xp[2] = x1;
					ps_imagefill (xp, yp, 4, f->pattern_no, f->pattern, f->inverse, f->dpi, FALSE, f->colorize, f->f_rgb, f->b_rgb);	/* Contours drawn separately below if desired */
				}
				else
					ps_rect (x0, 0.0, x1, width, GMT_lut[i].rgb_low, FALSE);
				x0 = x1;
			}
		}
		
		if (extend) {	/* Add color triangles for fore- and background */
			xp[0] = xp[1] = xleft;	xp[2] = xp[0] - e_length;
			yp[0] = width;	yp[1] = 0.0;	yp[2] = 0.5 * width;
			if ((f = GMT_bfn.fill[1]))
				ps_imagefill (xp, yp, 3, f->pattern_no, f->pattern, f->inverse, f->dpi, FALSE, f->colorize, f->f_rgb, f->b_rgb);	/* Contours drawn separately below if desired */
			else
				ps_polygon (xp, yp, 3, GMT_bfn.background_rgb, FALSE);
			ps_polygon (xp, yp, 3, GMT_no_rgb, 1);
			xp[0] = xp[1] = xright;	xp[2] = xp[0] + e_length;
			yp[0] = width;	yp[1] = 0.0;	yp[2] = 0.5 * width;
			if ((f = GMT_bfn.fill[0]))
				ps_imagefill (xp, yp, 3, f->pattern_no, f->pattern, f->inverse, f->dpi, FALSE, f->colorize, f->f_rgb, f->b_rgb);	/* Contours drawn separately below if desired */
			else
				ps_polygon (xp, yp, 3, GMT_bfn.foreground_rgb, FALSE);
			ps_polygon (xp, yp, 3, GMT_no_rgb, 1);
		}
		
		ps_plot (xleft, 0.0, 3);	ps_plotr (length, 0.0, 2);
		ps_plot (xleft, width, 3);	ps_plotr (length, 0.0, 2);
		ps_plot (xleft, 0.0, 3);	ps_plotr (0.0, width, 2);
		ps_plot (xright, 0.0, 3);	ps_plotr (0.0, width, 2);

		if (B_set) {	/* Used -B */
			GMT_x_axis (xleft, 0.0, length, GMT_lut[0].z_low, GMT_lut[GMT_n_colors-1].z_high, frame_info.anot_int[0], frame_info.frame_int[0], frame_info.label[0], frame_info.unit[0], LINEAR, 0, TRUE, frame_info.side[0]-1);
			dx = fabs (frame_info.grid_int[0]); 
			if (dx > 0.0) GMT_linearx_grid (GMT_lut[0].z_low, GMT_lut[GMT_n_colors-1].z_high, 0.0, width, dx);
		}
		else {
			anot_off = (((len > 0.0) ? len : 0.0) + gmtdefs.anot_offset);
			label_off = -(anot_off + 1.5 * gmtdefs.anot_offset + (gmtdefs.anot_font_size * GMT_u2u[GMT_PT][GMT_INCH]) * GMT_font_height[gmtdefs.anot_font]);
		
			/* First draw gridlines, unless skip_lines is TRUE */
			
			if (!skip_lines) {
				GMT_setpen (&gmtdefs.grid_pen);
				x1 = xleft;
				for (i = 0; i < GMT_n_colors; i++) {
					xx = (reverse) ? xright - x1 : x1;
					ps_plot (xx, 0.0, 3);
					ps_plotr (0.0, width, 2);
					x1 += z_width[i];
				}
				xx = (reverse) ? xleft : xright;
				ps_plot (xx, 0.0, 3);
				ps_plotr (0.0, width, 2);
			}
			
			/* Then anotate and draw tickmarks */
			
			GMT_setpen (&gmtdefs.tick_pen);
			x1 = xleft;
			for (i = 0; i < GMT_n_colors; i++) {
				xx = (reverse) ? xright - x1 : x1;
				ps_plot (xx, 0.0, 3);
				if (all || (GMT_lut[i].anot & 1)) {	/* Anotate this */
					ps_plotr (0.0, -len, 2);
					sprintf (text, format, GMT_lut[i].z_low);
					ps_text (xx, -anot_off, gmtdefs.anot_font_size, text, 0.0, 10, 0);
				}
				else
					ps_plotr (0.0, -len2, 2);
				x1 += z_width[i];
			}
			xx = (reverse) ? xleft : xright;
			ps_plot (xx, 0.0, 3);
			if (all || (GMT_lut[GMT_n_colors-1].anot & 2)) {
				ps_plotr (0.0, -len, 2);
				sprintf (text, format, GMT_lut[GMT_n_colors-1].z_high);
				ps_text (xx, -anot_off, gmtdefs.anot_font_size, text, 0.0, 10, 0);
			}
			else
				ps_plotr (0.0, -len2, 2);

			if (frame_info.label[0][0]) {
				ps_setfont (gmtdefs.label_font);
				ps_text (xleft+0.5*length, label_off, gmtdefs.label_font_size, frame_info.label[0], 0.0, 10, 0);
			}
		}
		if (frame_info.label[1][0]) {	/* Add unit label */
			ps_setfont (gmtdefs.anot_font);
			ps_text (xright+e_length+gmtdefs.anot_offset, 0.5*width, gmtdefs.anot_font_size, frame_info.label[1], 0.0, 5, 0);
		}
	}
	else {	/* Vertical scale */
		ps_transrotate (width, 0.0, 90.0);
		if (use_image) {	/* Must plot with image */
			if (monochrome || GMT_gray)
				ps_image (0.0, 0.0, length, width, bar, nx, ny, 8);
			else 
				GMT_color_image (0.0, 0.0, length, width, bar, nx, ny);
		}
		else {			/* Plot as rectangles */
			x0 = x1 = 0.0;
			yp[0] = yp[1] = 0.0;	yp[2] = yp[3] = width;
			for (i = 0; i < GMT_n_colors; i++) {
				x1 += z_width[i];
				if ((f = GMT_lut[i].fill)) {	/* Using pattern fills */
					xp[0] = xp[3] = x0;	xp[1] = xp[2] = x1;
					ps_imagefill (xp, yp, 4, f->pattern_no, f->pattern, f->inverse, f->dpi, FALSE, f->colorize, f->f_rgb, f->b_rgb);	/* Contours drawn separately below if desired */
				}
				else
					ps_rect (x0, 0.0, x1, width, GMT_lut[i].rgb_low, FALSE);
				x0 = x1;
			}
		}
		sprintf (text, "%d", (int)fabs (floor (GMT_lut[0].z_low)));
		sprintf (test, "%d", (int)fabs (ceil (GMT_lut[GMT_n_colors-1].z_high)));
		off = ((MAX ((int)strlen (text), (int)strlen (test)) + ndec) * 0.49 + ((ndec > 0) ? 0.3 : 0.0) + ((GMT_lut[0].z_low < 0.0) ? 0.3 : 0.0))
			* gmtdefs.anot_font_size * GMT_u2u[GMT_PT][GMT_INCH];
		anot_off = -(((len > 0.0) ? len : 0.0) + gmtdefs.anot_offset + off);
		label_off = anot_off - 0.7 * gmtdefs.anot_font_size * GMT_u2u[GMT_PT][GMT_INCH] - 1.5 * gmtdefs.anot_offset;
		if (extend) {	/* Add color triangles for fore- and background */
			xp[0] = xp[1] = xleft;	xp[2] = xp[0] - e_length;
			yp[0] = width;	yp[1] = 0.0;	yp[2] = 0.5 * width;
			if ((f = GMT_bfn.fill[1]))
				ps_imagefill (xp, yp, 3, f->pattern_no, f->pattern, f->inverse, f->dpi, FALSE, f->colorize, f->f_rgb, f->b_rgb);	/* Contours drawn separately below if desired */
			else
				ps_polygon (xp, yp, 3, GMT_bfn.background_rgb, FALSE);
			ps_polygon (xp, yp, 3, GMT_no_rgb, 1);
			xp[0] = xp[1] = xright;	xp[2] = xp[0] + e_length;
			yp[0] = width;	yp[1] = 0.0;	yp[2] = 0.5 * width;
			if ((f = GMT_bfn.fill[0]))
				ps_imagefill (xp, yp, 3, f->pattern_no, f->pattern, f->inverse, f->dpi, FALSE, f->colorize, f->f_rgb, f->b_rgb);	/* Contours drawn separately below if desired */
			else
				ps_polygon (xp, yp, 3, GMT_bfn.foreground_rgb, FALSE);
			ps_polygon (xp, yp, 3, GMT_no_rgb, 1);
		}
		ps_plot (xleft, 0.0, 3);	ps_plotr (length, 0.0, 2);
		ps_plot (xleft, width, 3);	ps_plotr (length, 0.0, 2);
		ps_plot (xleft, 0.0, 3);	ps_plotr (0.0, width, 2);
		ps_plot (xright, 0.0, 3);	ps_plotr (0.0, width, 2);
		if (B_set) {	/* Used -B */
			dx = fabs (frame_info.grid_int[0]); 
			if (dx > 0.0) GMT_linearx_grid (GMT_lut[0].z_low, GMT_lut[GMT_n_colors-1].z_high, 0.0, width, dx);
			ps_transrotate (0.0, 0.0, -90.0);
			GMT_y_axis (0.0, 0.0, length, GMT_lut[0].z_low, GMT_lut[GMT_n_colors-1].z_high, frame_info.anot_int[0], frame_info.frame_int[0], CNULL, CNULL, LINEAR, 0, FALSE, frame_info.side[0]-1);
			ps_rotatetrans (0.0, 0.0, 90.0);
		}
		else {
			if (!skip_lines) {	/* First draw gridlines */
				GMT_setpen (&gmtdefs.grid_pen);
				x1 = xleft;
				for (i = 0; i < GMT_n_colors; i++) {
					xx = (reverse) ? xright - x1 : x1;
					ps_plot (xx, 0.0, 3);
					ps_plotr (0.0, width, 2);
					x1 += z_width[i];
				}
				xx = (reverse) ? xleft : xright;
				ps_plot (xx, 0.0, 3);
				ps_plotr (0.0, width, 2);
			}
			
			/* Then anotate and draw tickmarks */
			
			GMT_setpen (&gmtdefs.tick_pen);
			x1 = xleft;
			for (i = 0; i < GMT_n_colors; i++) {
				xx = (reverse) ? xright - x1 : x1;
				ps_plot (xx, 0.0, 3);
				if (all || (GMT_lut[i].anot & 1)) {
					ps_plotr (0.0, -len, 2);
					sprintf (text, format, GMT_lut[i].z_low);
					ps_text (xx, anot_off, gmtdefs.anot_font_size, text, -90.0, 7, 0);
				}
				else
					ps_plotr (0.0, -len2, 2);
				x1 += z_width[i];
			}
			xx = (reverse) ? xleft : xright;
			ps_plot (xx, 0.0, 3);
			if (all || (GMT_lut[GMT_n_colors-1].anot & 2)) {
				ps_plotr (0.0, -len, 2);
				sprintf (text, format, GMT_lut[GMT_n_colors-1].z_high);
				ps_text (xx, anot_off, gmtdefs.anot_font_size, text, -90.0, 7, 0);
			}
			else
				ps_plotr (0.0, -len2, 2);
		}

		if (frame_info.label[0][0]) {
			ps_setfont (gmtdefs.label_font);
			size = 0.9 * gmtdefs.label_font_size * GMT_u2u[GMT_PT][GMT_INCH];
			x0 = 0.5 * (length + ((int)strlen (frame_info.label[0]) -1) * size);
			text[1] = 0;
			for (i = 0; i < (int)strlen (frame_info.label[0]); i++) {
				x1 = x0 - i * size;
				text[0] = frame_info.label[0][i];
				ps_text (x1, label_off, gmtdefs.label_font_size, text, -90.0, 6, 0);
			}
		}
		if (frame_info.label[1][0]) {	/* Add unit label */
			ps_setfont (gmtdefs.anot_font);
			ps_text (xright + gmtdefs.anot_offset + e_length, 0.5 * width, gmtdefs.anot_font_size, frame_info.label[1], -90.0, 2, 0);
		}
		ps_rotatetrans (-width, 0.0, -90.0);

	}
	if (use_image) GMT_free ((void *)bar);
}

double get_z (double x, double *width, int n)
{
	int i = 0;
	double tmp;
	
	tmp = width[0];
	while (i < n && x > tmp) tmp += width[++i];
	if (i == n) return (GMT_lut[GMT_n_colors-1].z_high);
	return (GMT_lut[i].z_low + (x - tmp + width[i]) * (GMT_lut[i].z_high - GMT_lut[i].z_low) / width[i]);
}

void fix_format (char *unit, char *format)
{
	int i, j;
	char text[80], new_format[BUFSIZ];
	
	/* Check if anotation units should be added */
	
	if (unit && unit[0]) {	/* Must append the unit string */
		if (!strchr (unit, '%'))	/* No percent signs */
			strncpy (text, unit, 80);
		else {
			for (i = j = 0; i < (int)strlen (unit); i++) {
				text[j++] = unit[i];
				if (unit[i] == '%') text[j++] = unit[i];
			}
			text[j] = 0;
		}
		if (text[0] == '-')	/* No space between anotation and unit */
			sprintf (new_format, "%s%s", format, &text[1]);
		else		/* 1 space between anotation and unit */
			sprintf (new_format, "%s %s", format, text);
		strcpy (format, new_format);
	}
}
