/*--------------------------------------------------------------------
 *	$Id: psscale.c,v 1.35 2004/09/16 18:12:13 pwessel Exp $
 *
 *	Copyright (c) 1991-2004 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 illuminated 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
 *		      10-NOV-2003. PW: Enhanced -E to set back or foreground only
 * Version:	4
 *
 */

#include "gmt.h"

int bit_dpi = 300;
BOOLEAN B_set = FALSE, flip_anot = FALSE;

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

main(int argc, char **argv)
{
	int i, j, n, extend = 0;
	
	BOOLEAN horizontal = FALSE, equi = FALSE, intens = FALSE, error = FALSE, interval_annot = FALSE;
	BOOLEAN skip_lines = 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, gap = 0.0;
	
	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 'A':
					flip_anot = TRUE;
					break;
				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':
					j = 2;
					if (argv[i][2] == 'b') {
						extend = 1;
						j = 3;
					}
					else if (argv[i][2] == 'f') {
						extend = 2;
						j = 3;
					}
					else
						extend = 3;
					if (argv[i][j]) e_length = GMT_convert_units (&argv[i][j], GMT_INCH);
					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;
					j = 2;
					if (argv[i][2] == 'i') interval_annot = TRUE, j = 3;
					if (argv[i][j]) gap = GMT_convert_units (&argv[i][j], GMT_INCH);
					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]> [-A] [-C<cpt_file>] [-E[b|f][<length>]] [-B<tickinfo>] [-I[<intens>]\n");
		fprintf(stderr, "\t[-K] [-L[i][<gap>]] [-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-A Place the desired annotations on the other side of the colorscale instead\n");
		fprintf (stderr, "\t-B Set scale annotation interval and label. Use y-label to set unit label\n");
		fprintf (stderr, "\t   If no annotation 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 annotated (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 annotate Lower, Upper, or Both color segment boundaries\n");
		fprintf (stderr, "\t-E add sidebar triangles for back- and foreground colors.\n");
		fprintf (stderr, "\t   Specify b(ackground) or f(oreground) to get one only [Default is both]\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. -B interval, if set, is ignored\n");
		fprintf (stderr, "\t   Append i to annotate the interval range instead of lower/upper.\n");
		fprintf (stderr, "\t   If <gap> is appended, we separate each rectangle by <gap> units and center each\n");
		fprintf (stderr, "\t   lower (z0) annotation on the rectangle.  Ignored if not a discrete cpt table.\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 consistent */
	
	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);

	if (equi && B_set) B_set = FALSE;	/* -L overrides intervals set with -B */
	
	GMT_put_history (argc, argv);	/* Update .gmtcommands4 */

	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 %g to %g\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%gi/%gi", 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.axis[0].item[0].interval == 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, gmtdefs.encoding.name, 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, gap, interval_annot, 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, int extend, double e_length, double gap, BOOLEAN interval_annot, BOOLEAN monochrome)
{
	int i, ii, j, ndec = -1, dec, depth, rgb[3], rrggbb[3];
	int nx, ny, nm, barmem, k, justify;
	BOOLEAN reverse, all = TRUE, use_image, center = FALSE;
	char format[64], text[64], test[64], unit[32];
	unsigned char *bar;
	double off, annot_off, label_off, len, len2, size, x0, x1, dx, xx, dir, y_base;
	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.annot_font[0]);

	gmtdefs.annot_offset[0] = fabs (gmtdefs.annot_offset[0]);	/* No 'inside' annotations allowed in colorbar */

	/* Find max decimals needed */
	

	if (B_set) {
		ndec = GMT_get_format (frame_info.axis[0].item[0].interval, frame_info.axis[0].unit, frame_info.axis[0].prefix, format);
	}
	else {
		for (i = 0; i < GMT_n_colors; i++) {
			if (GMT_lut[i].annot & 1) {
				if ((dec = GMT_get_format (GMT_lut[i].z_low, CNULL, CNULL, text)) > ndec) {
					strcpy (format, text);
					ndec = dec;
				}
			}
			if (GMT_lut[i].annot & 2) {
				if ((dec = GMT_get_format (GMT_lut[i].z_high, CNULL, CNULL, text)) > ndec) {
					strcpy (format, text);
					ndec = dec;
				}
			}
			if (GMT_lut[i].annot) all = FALSE;
		}
	}
	
	if (ndec == 0) {	/* Not -B and no decimals are needed */
		strcpy (format, gmtdefs.d_format);
		fix_format (frame_info.axis[0].unit, 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 ((gap > 0.0 || interval_annot) && !use_image) {	/* Want to center annotations for discrete colortable, using lower z0 value */
		center = skip_lines = TRUE;
		gap *= 0.5;
		if (interval_annot) sprintf (text, "%s - %s", format, format);
		strcpy (format, text);
	}

	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);

	unit[0] = 0;
	if (!strcmp (frame_info.axis[0].label, frame_info.axis[1].label)) frame_info.axis[1].label[0] = 0;	/* Defeat the auto-repeat of axis info */
	strcpy (unit, frame_info.axis[1].label);	/* Save possible unit here since we need to copy x-axis info to the y-axis for vertical axis */
			
	if (flip_anot) {
		justify = (horizontal) ? 2 : 5;
		y_base = width;
		dir = -1.0;
	}
	else {
		justify = (horizontal) ? 10 : 7;
		y_base = 0.0;
		dir = 1.0;
	}

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

	depth = (monochrome || GMT_gray) ? 8 : 24;
	if (horizontal) {
		if (use_image) {	/* Must plot as image */
			GMT_color_image (0.0, 0.0, length, width, bar, nx, ny, depth);
		}
		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 + gap;	xp[1] = xp[2] = x1 - gap;
					ps_imagefill (xp, yp, 4, f->pattern_no, f->pattern, f->inverse, f->dpi, center, f->colorize, f->f_rgb, f->b_rgb);	/* Contours drawn separately below if desired */
				}
				else {
					memcpy ((void *)rgb, (void *)GMT_lut[i].rgb_low, (size_t)(3 * sizeof (int)));
					if (monochrome) rgb[0] = rgb[1] = rgb[2] = YIQ (rgb);
					ps_rect (x0+gap, 0.0, x1-gap, width, rgb, center);
				}
				x0 = x1;
			}
		}
		
		if (extend & 1) {	/* Add color triangles for background color  */
			xp[0] = xp[1] = xleft - gap;	xp[2] = xp[0] - e_length;
			yp[0] = width;	yp[1] = 0.0;	yp[2] = 0.5 * width;
			if ((f = GMT_bfn[GMT_BGD].fill))
				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 {
				memcpy ((void *)rgb, (void *)GMT_bfn[GMT_BGD].rgb, (size_t)(3 * sizeof (int)));
				if (monochrome) rgb[0] = rgb[1] = rgb[2] = YIQ (rgb);
				ps_polygon (xp, yp, 3, rgb, FALSE);
			}
			ps_polygon (xp, yp, 3, GMT_no_rgb, 1);
		}
		if (extend & 2) {	/* Add color triangles for foreground color  */
			xp[0] = xp[1] = xright + gap;	xp[2] = xp[0] + e_length;
			yp[0] = width;	yp[1] = 0.0;	yp[2] = 0.5 * width;
			if ((f = GMT_bfn[GMT_FGD].fill))
				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 {
				memcpy ((void *)rgb, (void *)GMT_bfn[GMT_FGD].rgb, (size_t)(3 * sizeof (int)));
				if (monochrome) rgb[0] = rgb[1] = rgb[2] = YIQ (rgb);
				ps_polygon (xp, yp, 3, rgb, FALSE);
			}
			ps_polygon (xp, yp, 3, GMT_no_rgb, 1);
		}
		
		if (!center) {
			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 */
			if (flip_anot)
				GMT_xy_axis (xleft, width, length, GMT_lut[0].z_low, GMT_lut[GMT_n_colors-1].z_high, &frame_info.axis[0], FALSE, frame_info.side[0]-1);
			else
				GMT_xy_axis (xleft, 0.0, length, GMT_lut[0].z_low, GMT_lut[GMT_n_colors-1].z_high, &frame_info.axis[0], TRUE, frame_info.side[0]-1);
			dx =  GMT_get_map_interval (0, GMT_GRID_UPPER); 
			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 {
			annot_off = (((len > 0.0 && !center) ? len : 0.0) + gmtdefs.annot_offset[0]);
			label_off = -(annot_off + 1.5 * gmtdefs.annot_offset[0] + (gmtdefs.annot_font_size[0] * GMT_u2u[GMT_PT][GMT_INCH]) * GMT_font[gmtdefs.annot_font[0]].height);
		
			/* First draw gridlines, unless skip_lines is TRUE */
			
			if (!skip_lines) {
				GMT_setpen (&gmtdefs.grid_pen[0]);
				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 annotate and draw tickmarks */
			
			GMT_setpen (&gmtdefs.tick_pen);
			x1 = xleft;
			if (center) x1 += 0.5 * z_width[0];
			for (i = 0; i < GMT_n_colors; i++) {
				xx = (reverse) ? xright - x1 : x1;
				if (!center) ps_plot (xx, y_base, 3);
				if (all || (GMT_lut[i].annot & 1)) {	/* Annotate this */
					if (!center) ps_plotr (0.0, -(len * dir), 2);
					if (center && interval_annot)
						sprintf (text, format, GMT_lut[i].z_low, GMT_lut[i].z_high);
					else
						sprintf (text, format, GMT_lut[i].z_low);
					ps_text (xx, y_base - (dir * annot_off), gmtdefs.annot_font_size[0], text, 0.0, justify, 0);
				}
				else
					if (!center) ps_plotr (0.0, -(len2 * dir), -2);
				x1 += z_width[i];
			}
			xx = (reverse) ? xleft : xright;
			if (!center) {
				ps_plot (xx, 0.0, 3);
				if (all || (GMT_lut[GMT_n_colors-1].annot & 2)) {
					ps_plotr (0.0, -(len * dir), -2);
					sprintf (text, format, GMT_lut[GMT_n_colors-1].z_high);
					ps_text (xx, y_base - (dir * annot_off), gmtdefs.annot_font_size[0], text, 0.0, justify, 0);
				}
				else
					ps_plotr (0.0, -(len2 * dir), -2);
			}
			if (frame_info.axis[0].label[0]) {
				ps_setfont (gmtdefs.label_font);
				ps_text (xleft+0.5*length, y_base + (dir * label_off), gmtdefs.label_font_size, frame_info.axis[0].label, 0.0, justify, 0);
			}
		}
		if (unit[0]) {	/* Add unit label */
			ps_setfont (gmtdefs.annot_font[0]);
			ps_text (xright+e_length+gmtdefs.annot_offset[0], 0.5*width, gmtdefs.annot_font_size[0], unit, 0.0, 5, 0);
		}
	}
	else {	/* Vertical scale */
		ps_transrotate (width, 0.0, 90.0);
		if (use_image) {	/* Must plot with image */
			GMT_color_image (0.0, 0.0, length, width, bar, nx, ny, depth);
		}
		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 + gap;	xp[1] = xp[2] = x1 - gap;
					ps_imagefill (xp, yp, 4, f->pattern_no, f->pattern, f->inverse, f->dpi, center, f->colorize, f->f_rgb, f->b_rgb);	/* Contours drawn separately below if desired */
				}
				else {
					memcpy ((void *)rgb, (void *)GMT_lut[i].rgb_low, (size_t)(3 * sizeof (int)));
					if (monochrome) rgb[0] = rgb[1] = rgb[2] = YIQ (rgb);
					ps_rect (x0 + gap, 0.0, x1 - gap, width, rgb, center);
				}
				x0 = x1;
			}
		}
		if (center && interval_annot) {
			sprintf (text, "%d - %d", (int)fabs (floor (GMT_lut[0].z_low)), (int)fabs (ceil (GMT_lut[0].z_high)));
			sprintf (test, "%d - %d", (int)fabs (floor (GMT_lut[GMT_n_colors-1].z_low)), (int)fabs (ceil (GMT_lut[GMT_n_colors-1].z_high)));
		}
		else {
			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)) + (interval_annot + 1) * ndec) * 0.49 + ((ndec > 0) ? 0.3 : 0.0) + ((GMT_lut[0].z_low < 0.0) ? 0.3 : 0.0))
			* gmtdefs.annot_font_size[0] * GMT_u2u[GMT_PT][GMT_INCH];
		annot_off = -(((len > 0.0 && !center) ? len : 0.0) + gmtdefs.annot_offset[0] + off);
		label_off = annot_off - 0.7 * gmtdefs.annot_font_size[0] * GMT_u2u[GMT_PT][GMT_INCH] - 1.5 * gmtdefs.annot_offset[0];
		if (extend & 1) {	/* Add color triangles for background */
			xp[0] = xp[1] = xleft - gap;	xp[2] = xp[0] - e_length;
			yp[0] = width;	yp[1] = 0.0;	yp[2] = 0.5 * width;
			if ((f = GMT_bfn[GMT_BGD].fill))
				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 {
				memcpy ((void *)rgb, (void *)GMT_bfn[GMT_BGD].rgb, (size_t)(3 * sizeof (int)));
				if (monochrome) rgb[0] = rgb[1] = rgb[2] = YIQ (rgb);
				ps_polygon (xp, yp, 3, rgb, FALSE);
			}
			ps_polygon (xp, yp, 3, GMT_no_rgb, 1);
		}
		if (extend & 2) {	/* Add color triangles for foreground */
			xp[0] = xp[1] = xright + gap;	xp[2] = xp[0] + e_length;
			yp[0] = width;	yp[1] = 0.0;	yp[2] = 0.5 * width;
			if ((f = GMT_bfn[GMT_FGD].fill))
				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 {
				memcpy ((void *)rgb, (void *)GMT_bfn[GMT_FGD].rgb, (size_t)(3 * sizeof (int)));
				if (monochrome) rgb[0] = rgb[1] = rgb[2] = YIQ (rgb);
				ps_polygon (xp, yp, 3, rgb, FALSE);
			}
			ps_polygon (xp, yp, 3, GMT_no_rgb, 1);
		}
		if (!center) {
			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. Must kludge by copying x-axis and scaling to y since we must use GMT_xy_axis to draw a rotated x-axis... */
			dx = GMT_get_map_interval (0, GMT_GRID_UPPER); 
			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);
			memcpy ((void *)&frame_info.axis[1], (void *)&frame_info.axis[0], sizeof (struct PLOT_AXIS));
			frame_info.axis[1].label[0] = 0;
			d_swap (project_info.x_scale, project_info.y_scale);
			d_swap (project_info.x0, project_info.y0);
			for (i = 0; i < 5; i++) frame_info.axis[1].item[i].parent = 1;
			if (flip_anot)
				GMT_xy_axis (-width, 0.0, length, GMT_lut[0].z_low, GMT_lut[GMT_n_colors-1].z_high, &frame_info.axis[1], TRUE, frame_info.side[0]-1);
			else
				GMT_xy_axis (0.0, 0.0, length, GMT_lut[0].z_low, GMT_lut[GMT_n_colors-1].z_high, &frame_info.axis[1], 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[0]);
				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 annotate and draw tickmarks */
			
			GMT_setpen (&gmtdefs.tick_pen);
			x1 = xleft;
			if (center) x1 += 0.5 * z_width[0];
			for (i = 0; i < GMT_n_colors; i++) {
				xx = (reverse) ? xright - x1 : x1;
				if (!center) ps_plot (xx, y_base, 3);
				if (all || (GMT_lut[i].annot & 1)) {
					if (!center) ps_plotr (0.0, -(len * dir), -2);
					if (center && interval_annot)
						sprintf (text, format, GMT_lut[i].z_low, GMT_lut[i].z_high);
					else
						sprintf (text, format, GMT_lut[i].z_low);
					ps_text (xx, y_base + (dir * annot_off), gmtdefs.annot_font_size[0], text, -90.0, justify, 0);
				}
				else
					if (!center) ps_plotr (0.0, -(dir * len2), -2);
				x1 += z_width[i];
			}
			if (!center) {
				xx = (reverse) ? xleft : xright;
				ps_plot (xx, y_base, 3);
				if (all || (GMT_lut[GMT_n_colors-1].annot & 2)) {
					ps_plotr (0.0, -(len * dir), -2);
					sprintf (text, format, GMT_lut[GMT_n_colors-1].z_high);
					ps_text (xx, y_base + (dir * annot_off), gmtdefs.annot_font_size[0], text, -90.0, justify, 0);
				}
				else
					ps_plotr (0.0, -(len2 * dir), -2);
			}
		}

		if (frame_info.axis[0].label[0]) {
			ps_setfont (gmtdefs.label_font);
			if (strchr (frame_info.axis[0].label, '@')) {	/* Must set text horizontally */
				ps_text (xleft+0.5*length, y_base + (dir * label_off), gmtdefs.label_font_size, frame_info.axis[0].label, 0.0, justify, 0);
			}
			else {
				size = 0.9 * gmtdefs.label_font_size * GMT_u2u[GMT_PT][GMT_INCH];
				x0 = 0.5 * (length + ((int)strlen (frame_info.axis[0].label) -1) * size);
				text[1] = 0;
				for (i = 0; i < (int)strlen (frame_info.axis[0].label); i++) {
					x1 = x0 - i * size;
					text[0] = frame_info.axis[0].label[i];
					ps_text (x1, y_base + (dir * label_off), gmtdefs.label_font_size, text, -90.0, 6, 0);
				}
			}
		}
		if (unit[0]) {	/* Add unit label */
			ps_setfont (gmtdefs.annot_font[0]);
			ps_text (xright + gmtdefs.annot_offset[0] + e_length, 0.5 * width, gmtdefs.annot_font_size[0], unit, -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 annotation 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 annotation and unit */
			sprintf (new_format, "%s%s", format, &text[1]);
		else		/* 1 space between annotation and unit */
			sprintf (new_format, "%s %s", format, text);
		strcpy (format, new_format);
	}
}
