1 /* ======================================================= *
2  * Copyright 1998-2008 Stephen C. Grubb                    *
3  * http://ploticus.sourceforge.net                         *
4  * Covered by GPL; see the file ./Copyright for details.   *
5  * ======================================================= */
6 
7 /* PROC BARS - bargraphs and histograms */
8 
9 /* Improvements made by
10  * Michael D. Beynon (mdb) - beynon@cs.umd.edu
11  * 09/23/2001 : mdb - Bug fix: wrong position of cluster bar labels.
12  *                  - Bug fix: only use selected records for automatic x value
13  *                  - Changes to allow clustered segment bars.
14  *
15  * Note: These changes allow clustered bars to not require all data on one
16  *       line, and instead use select to pick the correct portion for each
17  *       clusterpos.
18  */
19 
20 #include "pl.h"
21 #define MAXSTACKFIELDS 40
22 #define MAXCLP 200
23 
24 static int do_label();
25 static int do_lwl();
26 static char stacklist[300] = ""; /* lenfields get appended to this list so that "stackfield: *"  can be used */
27 static int prevclust = 1; /* attempt to be smart about resetting stacklist on cluster change */
28 static int labelrot = 0;	/* label rotation */
29 
30 
31 /* =========================== */
32 int
PLP_bars_initstatic()33 PLP_bars_initstatic()
34 {
35 strcpy( stacklist, "" );
36 prevclust = 1;
37 labelrot = 0;
38 return( 0 );
39 }
40 
41 
42 /* =========================== */
43 int
PLP_bars()44 PLP_bars( )
45 {
46 char attr[NAMEMAXLEN], *line, *lineval;
47 int lvp, first;
48 
49 char *barcolor, *outline, *labeldetails, *backbox, *crossover, *selectex, *legendlabel, *thinbarline;
50 char *colorlist, *mapurl, *maplabel, *labelselectex, *constantlen, *constantloc;
51 char *lblpos, *numstrfmt, *labelword, *expandedurl, *expandedlabel;
52 int i, j, ix, ixx, nt, stat, align, lenfield, locfield, do_outline, showvals;
53 double adjx, adjy, halfw, x, y, y0, xleft, xright, barwidth, fval, cr, laby, taillen;
54 int stackf[MAXSTACKFIELDS];
55 int nstackf, ncluster, clusterpos, labelfld, lwl, reverse, stopfld;
56 int errbars, errlofld, errhifld, reflecterr; int result; int reverseorder, reversespecified, labelmaxlen;
57 double rlo, rhi, clustsep, ticlen, ytic, errbarmult, minlabel;
58 int trunc, y_endin, y0_endin, label0val, leftticfld, rightticfld, midticfld, lwl_mustfit;
59 int ncolorlp, taillengiven, barwidthfield, hidezerobars, ibar, colorfield, irow, segmentflag, exactcolorfield;
60 char buf[256], tok[80], *colorlp[MAXCLP], axis, baseax;
61 char rangelo[40], rangehi[40], dcolor[COLORLEN], labelstr[128], colorbuf[COLORLEN+1];
62 
63 TDH_errprog( "pl proc bars" );
64 
65 
66 /* initialize */
67 axis = 'y';
68 
69 lenfield = locfield = labelfld = errlofld = errhifld = -1;
70 stopfld = leftticfld = rightticfld = midticfld = -1;
71 barwidthfield = colorfield = exactcolorfield = -1;
72 
73 barcolor = "0.7";
74 outline = "yes";
75 
76 labeldetails = ""; crossover = ""; backbox = ""; labelword = ""; selectex = "";
77 labelselectex = ""; thinbarline = ""; colorlist = ""; lblpos = "";
78 mapurl = ""; constantlen = ""; constantloc = ""; maplabel = ""; legendlabel = "";
79 
80 strcpy( rangelo, "" ); strcpy( rangehi, "" );
81 
82 numstrfmt = "%g";
83 
84 showvals = 0; nstackf = 0; lwl = 0; errbars = 0; reflecterr = 0; reverseorder = 0; reversespecified = 0;
85 trunc = 0; label0val = 0; ncolorlp = 0; taillengiven = 0; hidezerobars = 0; segmentflag = 0;
86 labelrot = 0; lwl_mustfit = 0;
87 
88 do_outline = 1;
89 barwidth = 0.0;
90 
91 ncluster = 1;
92 clusterpos = 1;
93 taillen = 0.0;
94 clustsep = 0.0;
95 ticlen = 0.02;
96 errbarmult = 1.0;
97 minlabel = NEGHUGE;
98 labelmaxlen = 250;
99 
100 
101 /* get attributes.. */
102 first = 1;
103 while( 1 ) {
104 	line = getnextattr( first, attr, &lvp );
105 	if( line == NULL ) break;
106 	first = 0;
107 	lineval = &line[lvp];
108 
109 	if( strcmp( attr, "lenfield" )==0 ) lenfield = fref( lineval ) -1;
110 	else if( strcmp( attr, "locfield" )==0 ) locfield = fref( lineval ) -1;
111 	else if( strcmp( attr, "axis" )==0 ) axis = lineval[0];
112 	else if( strcmp( attr, "horizontalbars" )==0 ) axis = 'x';
113 	else if( strcmp( attr, "color" )==0 ) barcolor = lineval;
114 	else if( strcmp( attr, "outline" )==0 ) outline = lineval;
115 	else if( strcmp( attr, "barwidth" )==0 ) { barwidth = ftokncpy( lineval ); if( PLS.usingcm ) barwidth /= 2.54; }
116 	else if( strncmp( attr, "stackfield", 10 )==0 ) {
117 		for( ix = 0, j = 0; j < MAXSTACKFIELDS; j++ ) {
118                         if( GL_smember( lineval, "* all" )) strcpy( buf, GL_getok( stacklist, &ix ) );
119                         else strcpy( buf, GL_getok( lineval, &ix ) );
120 			if( buf[0] == '\0' ) break;
121 			stackf[j] = fref( buf );
122 			}
123 		nstackf = j;
124 		}
125 	else if( strcmp( attr, "cluster" )==0 ) {
126 		nt = sscanf( lineval, "%d %s %d", &clusterpos, buf, &ncluster );
127 		if( nt == 2 ) sscanf( lineval, "%d %d", &clusterpos, &ncluster );
128 		}
129 	else if( strcmp( attr, "clustersep" )==0 ) { clustsep = ftokncpy( lineval ); if( PLS.usingcm ) clustsep /= 2.54; }
130 	else if( strcmp( attr, "crossover" )==0 ) crossover = lineval;
131 	else if( strncmp( attr, "constantlen", 11 )==0 ) constantlen = lineval;
132 	else if( strncmp( attr, "constantloc", 11 )==0 ) constantloc = lineval;
133 	else if( strncmp( attr, "segmentfield", 12 )==0 ) {
134 		for( i = 0, ix = 0; ; i++ ) {
135 			strcpy( buf, GL_getok( lineval, &ix ));
136 			if( buf[0] == '\0' ) break;
137 			if( i == 0 ) stopfld = fref( buf );
138 			else if( i == 1 ) { stackf[0] = stopfld; nstackf = 1; stopfld = fref( buf ); }
139 			}
140 		segmentflag = 1;
141 		}
142 	else if( strncmp( attr, "errbarfield", 11 )==0 ) {
143 		char fname[2][50];
144 		errbars = 1;
145 		nt = sscanf( lineval, "%s %s", fname[0], fname[1] );
146 		if( strcmp( fname[0], "0" )==0 ) {  /* allow oneway error bars   scg 4/11/04 */
147 			if( nt == 1 ) { Eerr( 3845, "incorrect errbarfield spec", "" ); errbars = 0; }
148 			else errlofld = 0;
149 			}
150 		else errlofld = fref( fname[0] );
151 		if( nt == 1 ) reflecterr = 1; /* use -val for lo, +val for hi */
152 		else 	{
153 			reflecterr = 0;
154 			errhifld = fref( fname[1] );
155 			}
156 		}
157 	else if( strncmp( attr, "errbarmult", 10 )==0 ) errbarmult = ftokncpy( lineval );
158 	else if( strcmp( attr, "tails" )==0 ) { taillen = ftokncpy( lineval ); taillengiven = 1; if( PLS.usingcm ) taillen /= 2.54; }
159 	else if( strcmp( attr, "showvalues" )==0 ) showvals = getyn( lineval );
160 	else if( strcmp( attr, "numbersformat" )==0 ) numstrfmt = lineval;
161 	else if( strcmp( attr, "labelzerovalue" )==0 ) label0val = getyn( lineval );
162 	else if( strcmp( attr, "minlabel" )==0 ) minlabel = ftokncpy( lineval );
163 	else if( strcmp( attr, "truncate" )==0 ) trunc = getyn( lineval );
164 	else if( strcmp( attr, "labeldetails" )==0 ) labeldetails = lineval;
165 	else if( strcmp( attr, "backbox" )==0 ) backbox = lineval;
166 	else if( strcmp( attr, "labelfield" )==0 ) labelfld = fref( lineval ) - 1;
167 	else if( strcmp( attr, "labelword" )==0 ) labelword = lineval;
168 	else if( strcmp( attr, "thinbarline" )==0 ) thinbarline = lineval;
169 	else if( strcmp( attr, "leftticfield" )==0 ) leftticfld = fref( lineval ) -1;
170 	else if( strcmp( attr, "rightticfield" )==0 ) rightticfld = fref( lineval ) -1;
171 	else if( strcmp( attr, "midticfield" )==0 ) midticfld = fref( lineval ) -1;
172 	else if( strcmp( attr, "ticlen" )==0 ) ticlen = ftokncpy( lineval );
173 	else if( strcmp( attr, "reverseorder" )==0 ) { reverseorder = getyn( lineval ); reversespecified = 1; }
174 	else if( strcmp( attr, "hidezerobars" )==0 )  hidezerobars = getyn( lineval );
175 
176 	else if( strcmp( attr, "barsrange" )==0 ) sscanf( lineval, "%s %s", rangelo, rangehi );
177 	else if( strcmp( attr, "colorlist" )==0 ) colorlist = lineval;
178 	else if( strcmp( attr, "colorfield" )==0 ) colorfield = fref( lineval ) -1;
179 	else if( strcmp( attr, "exactcolorfield" )==0 ) exactcolorfield = fref( lineval ) -1;
180 	else if( strcmp( attr, "longwayslabel" )==0 ) lwl = getyn( lineval );
181 	else if( strcmp( attr, "labelmustfit" )==0 ) {
182 		if( strcmp( lineval, "omit" )==0 ) lwl_mustfit = 1;
183 		else if( strcmp( lineval, "truncate" )==0 ) lwl_mustfit = 2;
184 		else lwl_mustfit = 0;
185 		}
186 	else if( strcmp( attr, "labelmaxlen" )==0 ) labelmaxlen = itokncpy( lineval );
187 	else if( strncmp( attr, "labelrot", 8 )==0 ) labelrot = itokncpy( lineval );
188 	else if( strcmp( attr, "select" )==0 ) selectex = lineval;
189 	else if( strcmp( attr, "labelselect" )==0 ) labelselectex = lineval;
190 	else if( strcmp( attr, "legendlabel" )==0 ) legendlabel = lineval;
191 	else if( strcmp( attr, "labelpos" )==0 ) lblpos = lineval;
192 	else if( strcmp( attr, "barwidthfield" )==0 ) barwidthfield = fref( lineval ) -1;
193 	else if( strcmp( attr, "clickmapurl" )==0 ) mapurl = lineval;
194 	else if( strcmp( attr, "clickmaplabel" )==0 ) maplabel = lineval;
195 	else if( strcmp( attr, "clickmaplabeltext" )==0 ) maplabel = getmultiline( lineval, "get" );
196 	else Eerr( 1, "attribute not recognized", attr );
197 	}
198 
199 
200 /* -------------------------- */
201 /* overrides and degenerate cases */
202 /* -------------------------- */
203 if( axis == 'y' ) baseax = 'x';
204 else baseax = 'y';
205 
206 if( Nrecords < 1 ) return( Eerr( 17, "No data has been read yet w/ proc getdata", "" ) );
207 if( !scalebeenset() ) return( Eerr( 51, "No scaled plotting area has been defined yet w/ proc areadef", "" ) );
208 
209 if( locfield > Nfields ) return( Eerr( 52, "locfield out of range", "" ) );
210 if( lenfield > Nfields ) return( Eerr( 52, "lenfield out of range", "" ) );
211 if( lenfield < 0 && !segmentflag && constantlen[0] == '\0' ) return( Eerr( 2805, "Either lenfield, segmentfields, or constantlen must be defined", ""));
212 
213 if( stopfld >= 0 ) {
214 	if( nstackf > 1 )  { Eerr( 2984, "stackfield may not be used with segments", "" ); nstackf=0; }
215 	}
216 
217 if( labelword[0] != '\0' ) showvals = 1;
218 if( showvals && labelword[0] == '\0' ) labelword = "@N";
219 
220 if( locfield == lenfield && locfield >= 0 ) Eerr( 2479, "Warning, locfield same as lenfield", "" );
221 
222 for( i = 0; i < nstackf; i++ ) {
223 	if( (lenfield+1) == stackf[i] ) Eerr( 2479, "Warning, lenfield same as a stackfield", "" );
224 	if( (locfield+1) == stackf[i] ) Eerr( 2479, "Warning, locfield same as a stackfield", "" );
225 	}
226 
227 if( axis == 'x' && !reversespecified ) reverseorder = 1;
228 
229 if( strncmp( legendlabel, "#usefname", 9 )==0 ) getfname( lenfield+1, legendlabel );
230 
231 if( segmentflag ) lwl = 1;  /* when doing floating segment bars, default to use labels that are centered within the bar - scg 5/8/06 */
232 
233 
234 /* -------------------------- */
235 /* now do the plotting work.. */
236 /* -------------------------- */
237 if( baseax == 'y' ) Eflip = 1;
238 
239 if( rangelo[0] != '\0' ) rlo = Econv( baseax, rangelo );
240 else rlo = Elimit( baseax, 'l', 's' );
241 if( rangehi[0] != '\0' ) rhi = Econv( baseax, rangehi );
242 else rhi = Elimit( baseax, 'h', 's' );
243 
244 /* maintain stacklist */
245 if( clusterpos != prevclust ) {
246 	PL_resetstacklist();
247 	if( !segmentflag ) nstackf = 0; /* needed for current bar */
248 	}
249 prevclust = clusterpos;
250 sprintf( buf, "%d ", lenfield+1 );
251 strcat( stacklist, buf );
252 
253 
254 if( barwidth > 0.0 ) halfw = barwidth * 0.5;
255 else	{
256 	if( ncluster <= 1 ) halfw = ( Ea( X, 1.0 ) - Ea( X, 0.0 ) ) * 0.4;
257 	else if( ncluster > 1 ) halfw = (( Ea( X, 1.0 ) - Ea( X, 0.0 ) ) * 0.4)/ (double)ncluster;
258 	if( halfw > 0.5 ) halfw = 0.1; /* sanity  - to prevent huge bars */
259 	}
260 
261 
262 if( outline[0] == '\0' || strncmp( outline, "no", 2 )==0 ) do_outline = 0;
263 else do_outline = 1;
264 
265 
266 if( crossover[0] == '\0' ) cr = Elimit( axis, 'l', 's' );
267 else cr = Econv( axis, crossover );
268 if( cr < Elimit( axis, 'l', 's' )) cr = Elimit( axis, 'l', 's' );  /* be sure crossover is in range .. added scg 8/25/04 */
269 if( cr > Elimit( axis, 'h', 's' )) cr = Elimit( axis, 'h', 's' );  /* be sure crossover is in range .. added scg 8/25/04 */
270 
271 
272 /* parse colorlist if any */
273 if( colorlist[0] != '\0' ) {
274 	/* initialize all pointers to default color.. */
275 	strcpy( dcolor, barcolor );
276 	for( i = 0; i < MAXCLP; i++ ) colorlp[i] = dcolor;
277 	ix = 0; ixx = 0;
278 	i = 0;
279 	while( 1 ) {
280 		strcpy( tok, GL_getok( colorlist, &ix ) );
281 		if( tok[0] == '\0' ) break;
282 		if( atoi( tok ) > 0 && atoi( tok ) < MAXCLP ) {
283 			colorlp[ atoi(tok) - 1 ] = &colorlist[ix];
284 			GL_getok( colorlist, &ix );
285 			}
286 		else if( i < MAXCLP ) {
287 			colorlp[ i ] = &colorlist[ ixx ];
288 			i++;
289 			}
290 		ixx = ix;
291 		}
292 	}
293 
294 linedet( "outline", outline, 0.5 );
295 PLG_forcecolorchg();
296 
297 if( thinbarline[0] != '\0' && strncmp( thinbarline, "no", 2 ) != 0 ) linedet( "thinbarline", thinbarline, 0.3 );
298 
299 if( errbars && !taillengiven ) taillen = 0.2; /* set a default taillen for errorbars */
300 
301 
302 /* ---------------- */
303 /* loop through current data set, draw bars.. */
304 /* ---------------- */
305 ibar = -1;
306 for( irow = 0; irow < Nrecords; irow++ ) {
307 
308 	if( selectex[0] != '\0' ) { /* process against selection condition if any.. */
309                 stat = do_select( selectex, irow, &result );
310                 if( stat != 0 ) { Eerr( stat, "Select error", selectex ); continue; }
311                 if( result == 0 ) continue; /* reject */
312                 }
313 	ibar++;
314 
315 
316 	if( lenfield >= 0 ) {
317 		y = fda( irow, lenfield, axis );
318 		if( Econv_error() ) { conv_msg( irow, lenfield, "lenfield" ); continue; }
319 		}
320 	else if( constantlen[0] != '\0' ) y = Econv( axis, constantlen );
321 
322 	if( constantloc[0] != '\0' ) x = Econv( baseax, constantloc );
323 	else if( locfield < 0 ) {
324 		if( reverseorder ) x = (Elimit( baseax, 'h', 's' ) - 1.0) - (double)ibar;
325 		else x = (double)ibar+1;
326 		if( x > Elimit( baseax, 'h', 's' ) ) {
327 			fprintf( PLS.errfp, "bars warning, skipping bar# %d, loc is out of range\n", ibar+1 );
328 			continue; /* out of range hi */
329 			}
330 		}
331 	else 	{
332 		x = fda( irow, locfield, baseax );
333 		if( Econv_error() ) { conv_msg( irow, locfield, "locfield" ); continue; }
334 		if( x < rlo ) continue;  /* out of range low */
335 		if( x > rhi ) continue;  /* out of range high */
336 		}
337 
338 	/* y0 = Elimit( axis, 'l', 's' ); */
339 	if( nstackf > 0 && !segmentflag ) y0 = 0.0; /* added scg 11/27/01 */
340 	else y0 = cr;
341 
342 
343 	/* if barwidthfield was specified, set bar width now.. */
344 	if( barwidthfield >= 0 ) {
345 		double bw;
346 		bw = fda( irow, barwidthfield, axis );
347 		halfw = (Ea( X, bw ) - Ea( X, 0.0 )) * 0.5;
348 		if( Econv_error() ) {
349 			conv_msg( irow, barwidthfield, "barwidthfield" );
350 			halfw = 0.05;
351 			}
352 		}
353 
354 	if( nstackf > 0 ) /* stacking */
355 		for( j = 0; j < nstackf; j++ ) {
356 			/* BDB: here is where scott barrett's bug seems to occur.. */
357 			/* Digital UNIX 4.0g on a Compaq ES-40 Server.  debugger indicates abend in proc_bars.c */
358 			fval = fda( irow, stackf[j]-1, axis );
359 			if( segmentflag ) fval -= cr; /* normalize? */
360 			y0 += fval;
361 			if( !segmentflag ) y += fval; /* condition added scg 9/26/03 .. because y is undefined at this point */
362 			}
363 
364 
365 	if( ncluster > 1 ) {   /* clustering - move sideways a little.. */
366 		xleft = Ea( X, x ) - ((halfw+clustsep) * (double)(ncluster));
367 		xleft += clustsep;
368 		if( baseax == Y ) xleft += ((halfw+clustsep) * (ncluster-clusterpos)*2.0);
369 		else xleft += ((halfw+clustsep) * (clusterpos-1)*2.0);
370 		xright = xleft + (halfw*2.0);
371 		}
372 	else	{
373 		xleft = Ea( X, x) - halfw;
374 		xright = Ea( X, x) + halfw;
375 		}
376 
377 	y_endin = y0_endin = 1;
378 
379 	if( segmentflag ) { /* set y from stopfld; bar start is done via stacking, above */
380 		y = fda( irow, stopfld-1, axis );
381 		}
382 	if( errbars ) { /* set y and y0 as offsets from original y */
383 		double eblen;
384 		if( errlofld == 0 ) { y0 = y; y0_endin = 0; }
385 		else 	{
386 			eblen = (fda( irow, errlofld-1, axis ) * errbarmult);
387 			if( y < cr ) y0 = y + eblen;
388 			else y0 = y - eblen;
389 			}
390 		if( reflecterr ) eblen = fda( irow, errlofld-1, axis ) * errbarmult;
391 		else eblen = fda( irow, errhifld-1, axis ) * errbarmult;
392 		if( y < cr ) y -= eblen; /* downward/leftward bar.. reverse direction */
393 		else y += eblen;      /* normal */
394 		}
395 
396 	/* catch units errors for stopfld and errflds.. */
397 	if( segmentflag && Econv_error() ) {conv_msg( irow, stopfld, "segmentfields" );continue;}
398 	if( errbars && Econv_error() ){conv_msg( irow, stopfld, "errbarfields" );continue;}
399 
400 	/* null-length bars.. skip out.. scg 11/29/00 */
401 	if( hidezerobars && y == y0 ) continue;
402 
403 	/* truncate to plotting area.. scg 5/12/99 */
404 	if( trunc ) {
405 
406 		if( y0 <= Elimit( axis, 'l', 's' ) && y < Elimit( axis, 'l', 's' ) ) {
407 			fprintf( PLS.errfp, "warning, bar completely out of %c plotting area\n", axis );
408 			continue; /* skip entirely */
409 			}
410 		if( y0 >= Elimit( axis, 'h', 's' ) && y > Elimit( axis, 'h', 's' ) ) {
411 			fprintf( PLS.errfp, "warning, bar completely out of %c plotting area\n", axis );
412 			continue; /* skip entirely */
413 			}
414 
415 		if( !Ef_inr( axis, y0 ) ) {
416 			if( y0 < y ) y0 = Elimit( axis, 'l', 's' );
417 			else y0 = Elimit( axis, 'h', 's' );
418 			y0_endin = 0;
419 			}
420 		if( !Ef_inr( axis, y ) ) {
421 			if( y0 < y ) y = Elimit( axis, 'h', 's' );
422 			else y = Elimit( axis, 'l', 's' );
423 			y_endin = 0;
424 			}
425 		}
426 
427 	/* if colorfield used, get color.. */
428 	if( colorfield >= 0 )  barcolor = PL_get_legent( da( irow, colorfield ) );
429 	else if( exactcolorfield >= 0 ) barcolor = da( irow, exactcolorfield );
430 
431 
432 	/* if colorlist used, get color.. */
433 	if( colorlist[0] != '\0' && ibar < MAXCLP ) {
434 		sscanf( colorlp[ibar], "%s", colorbuf );
435 		barcolor = colorbuf;
436 		}
437 
438 	/* now do the bar.. */
439 
440 	/* allow @field substitutions into url */
441 	if( PLS.clickmap && ( mapurl != "" || maplabel != "" )) {
442 		expandedurl = PL_bigbuf;
443 		expandedlabel = &PL_bigbuf[2000];
444 		do_subst( expandedurl, mapurl, irow, URL_ENCODED );
445 		do_subst( expandedlabel, maplabel, irow, NORMAL );
446 		}
447 
448 
449 	/* if thinbarline specified, or if doing error bars, render bar as a line */
450 	if( ( thinbarline[0] != '\0' && strncmp( thinbarline, "no", 2 )!= 0 ) || errbars ) {
451 		Emov( xleft+halfw, Ea( Y, y0 ) );
452 		Elin( xleft+halfw, Ea( Y, y ) );
453 		}
454 
455   	/* otherwise, render bar as a rectangle */
456   	else 	{
457 		Ecblock( xleft, Ea( Y, y0 ), xright, Ea( Y, y ), barcolor, 0 );
458 
459 		if( do_outline ) {   /* render bar outline.. but no outline where truncated.. added scg 5/11/06 */
460 			Emov( xleft, Ea( Y, y0 ) );
461 			Elin( xleft, Ea( Y, y ) );
462 			if( y_endin ) Elin( xright, Ea( Y, y ) );
463 			else Emov( xright, Ea( Y, y ) );
464 			Elin( xright, Ea( Y, y0 ) );
465 			if( y0_endin ) Elin( xleft, Ea( Y, y0 ) );
466 			}
467 
468 		if( PLS.clickmap && (mapurl != "" || maplabel != "" )) {
469 			if( Eflip ) clickmap_entry( 'r', expandedurl, 0, Ea( Y, y0 ), xleft, Ea( Y, y ), xright, 0, 0, expandedlabel );
470 			else clickmap_entry( 'r', expandedurl, 0, xleft, Ea( Y, y0 ), xright, Ea( Y, y ), 0, 0, expandedlabel );
471 			}
472 
473 		}
474 
475   	/* do tics if requested */  /* don't do if trunc && outside area - scg 11/21/00 */
476 	/* Bug fix - ticks not being drawn at bars when truncating was not switched on.
477          * Supplied by Michael Rausch (mr@netadair.de) date: 04 Jun 2001 */
478   	if( leftticfld >= 0 ) {
479   		ytic = fda( irow, leftticfld, axis );
480   		if( !Econv_error() && ( !trunc || Ef_inr( axis, ytic ) ) ) {
481   			Emov( (xleft+halfw), Ea(Y,ytic) );
482   			Elin( (xleft+halfw)-ticlen, Ea(Y,ytic) );
483   			}
484   		}
485   	if( rightticfld >= 0 ) {
486   		ytic = fda( irow, rightticfld, axis );
487   		if( !Econv_error() && ( !trunc || Ef_inr( axis, ytic ) ) ) {
488   			Emov( (xleft+halfw), Ea(Y,ytic) );
489   			Elin( (xleft+halfw)+ticlen, Ea(Y,ytic) );
490   			}
491   		}
492   	if( midticfld >= 0 ) {
493   		ytic = fda( irow, midticfld, axis );
494   		if( !Econv_error() && ( !trunc || Ef_inr( axis, ytic ) ) ) {
495   			Emov( (xleft+halfw)-(ticlen/2.0), Ea(Y,ytic) );
496   			Elin( (xleft+halfw)+(ticlen/2.0), Ea(Y,ytic) );
497   			}
498 		}
499 
500 
501 	/* do tails if requested */
502 	if( taillen > 0.0 ) {
503 		double g, h;
504 		g = xleft + ((xright-xleft)  / 2.0);
505 		h = taillen / 2.0;
506 		if( y_endin ) { Emov( g-h, Ea(Y,y) ); Elin( g+h, Ea(Y,y) ); }
507 		if( y0_endin ) { Emov( g-h, Ea(Y,y0) ); Elin( g+h, Ea(Y,y0) ); }
508 		}
509 
510 	}
511 
512 
513 
514 /* ---------------- */
515 /* now add labels if any */
516 /* ---------------- */
517 
518 if( showvals || labelfld >= 0 ) {
519 	textdet( "labeldetails", labeldetails, &align, &adjx, &adjy, -3, "R", 1.0 );
520 	if( adjy == 0.0 ) adjy = 0.02; /* so label is a little above end of bar */
521 	if( align == '?' ) align = 'C';
522 	ibar = -1;
523 	for( i = 0; i < Nrecords; i++ ) {
524 
525 
526 		if( selectex[0] != '\0' ) { /* added 8/23/01 - process against selection condition if any.. */
527                 	stat = do_select( selectex, i, &result );
528                 	if( stat != 0 ) { Eerr( stat, "Select error", selectex ); continue; }
529                 	if( result == 0 ) continue; /* reject */
530                 	}
531 		ibar++;
532 
533 		if( labelselectex[0] != '\0' ) { /* process against label selection condition if any.. added scg 5/11/06 */
534                 	stat = do_select( labelselectex, i, &result );
535                 	if( stat != 0 ) { Eerr( stat, "Select error", selectex ); continue; }
536                 	if( result == 0 ) continue; /* reject */
537 			}
538 
539 		if( lenfield >= 0 ) {
540 			y = fda( i, lenfield, axis );
541 			if( Econv_error() ) continue; /* don't bother to label bad values */
542 			if( !label0val && GL_close_to( y, cr, 0.000001 )) continue; /* don't label 0 */
543 			if( y < minlabel ) continue;   /* suppress labels for small bars , added 5/4/04, thanks to Jessika Feustel */
544 			}
545 
546 		if( constantloc[0] != '\0' ) x = Econv( baseax, constantloc );
547 		else if( locfield < 0 ) {
548 		        if( reverseorder ) x = (Elimit( baseax, 'h', 's' ) - 1.0) - (double)ibar;
549 			else x = (double)ibar+1;
550 			}
551 		else 	{
552 			x = fda( i, locfield, baseax );
553 			if( Econv_error() ) continue; /* don't bother to label bad values - added scg 8/10/05 */
554 			if( x < rlo ) continue; /* out of range low */
555 			if( x > rhi ) continue; /* out of range high */
556 			}
557 
558 
559 		/* compose label.. */
560 		if( labelfld >= 0 ) strcpy( labelstr, da( i, labelfld ) );
561 		else 	{
562 			if( segmentflag ) y = fda( i, stopfld-1, axis ); /* get y now.. scg 9/27/04 */
563 			strcpy( labelstr, labelword );
564 			}
565 		stat = Euprint( buf, axis, y, numstrfmt );
566 		GL_varsub( labelstr, "@N", buf );
567 
568 
569 		/* check / truncate length.. */
570 		if( strlen( labelstr ) > labelmaxlen ) {
571 			labelstr[ labelmaxlen+2 ] = '\0';
572 			labelstr[ labelmaxlen+1 ] = '.';
573 			labelstr[ labelmaxlen ] = '.';
574 			}
575 
576 		fval = cr; /* needed in case we want to center long text label along len of bar */
577 		if( nstackf > 0 ) {   /* stacking affects label placement */
578 			double ff;
579 			for( j = 0; j < nstackf; j++ ) {
580 				ff = fda( i, stackf[j]-1, axis );
581 				if( !segmentflag ) fval += ff;	/* scg 4/28/04 */
582 				else fval = ff;
583 				/* fval is used below to center longwise labels */
584 				if( segmentflag ) y += (ff - cr);
585 				else y += ff;
586 				}
587 			}
588 
589 		if( segmentflag ) {  /* set y from stopfld; bar start is done via stacking, above */
590 			y = fda( i, stopfld-1, axis );
591 			if( Econv_error() ) continue;
592 			}
593 
594 		if( errbars ) { /* set y and y0 as offsets from original y */
595 			if( errlofld == 0 ) y0 = y;
596 			else y0 = y - fda( i, errlofld+1, axis );
597 			if( reflecterr ) y += fda( i, errlofld-1, axis );
598 			else y += fda( i, errhifld+1, axis );
599 			if( Econv_error() ) continue;
600 			}
601 
602 
603 		/* truncate to plotting area.. scg 5/12/99 */
604 		if( trunc ) {
605 
606 			/* if bar completely out of plotting area, omit  - added scg 8/10/05 */
607 			if( y < Elimit( axis, 'l', 's' ) ) continue;
608 
609 			if( lwl ) {  /* longways labels.. revise bar start & stop so label is properly centered - added scg 5/10/06 */
610 				if( fval > Elimit( axis, 'h', 's' )) continue;  /* bar completely off hi end.. omit */
611 				if( y > Elimit( axis, 'h', 's' )) y = Elimit( axis, 'h', 's' );
612 				if( fval < Elimit( axis, 'l', 's' )) fval = Elimit( axis, 'l', 's' );
613 				}
614 			else if( y > Elimit( axis, 'h', 's' ) ) continue;  /* for regular labels, if top of bar is off, don't show it */
615 
616 			if( !Ef_inr( axis, y ) ) {
617 				if( y > Elimit( axis, 'h', 's' ) )  y = Elimit( axis, 'h', 's' );
618 				else laby = y = Elimit( axis, 'l', 's' );
619 				}
620 			}
621 
622 		if( y < cr ) {
623 			laby = Ea( Y, y ) + (adjy*(-1.0));
624 			if( !Eflip ) laby -= Ecurtextheight;
625 			reverse = 1;
626 			}
627 		else 	{
628 			laby = (Ea( Y, y )+adjy);
629 			reverse = 0;
630 			}
631 
632 
633 		/* if explicit label position given, use it.. */
634 		if( lblpos[0] != '\0' ) Eposex( lblpos, axis, &laby );
635 
636 
637 		if( ncluster > 1 ) { /* if clusters, move sideways a little bit.. */
638 			x = Ea( X, x ) - ((halfw+clustsep) * (double)(ncluster));
639 			x += clustsep;
640 		        if( baseax == Y ) x += ((halfw+clustsep) * (ncluster-clusterpos)*2.0);
641 			else x += ((halfw+clustsep) * (clusterpos-1)*2.0);
642 			x += halfw;
643 			if( lwl ) do_lwl( labelstr, x+adjx, Ea(Y,y)+adjy, Ea(Y,fval)+adjy, align, reverse, lwl_mustfit );
644 			else do_label( labelstr, x+adjx, laby, align, backbox, reverse );
645 			}
646 		else 	{
647 			if( lwl ) do_lwl( labelstr, Ea(X,x)+adjx, Ea(Y,y)+adjy, Ea(Y,fval)+adjy, align, reverse, lwl_mustfit );
648 			else do_label( labelstr, Ea(X,x)+adjx, laby, align, backbox, reverse );
649 			}
650 		}
651 	}
652 
653 
654 
655 if( legendlabel[0] != '\0' ) {
656 	if( errbars || ( thinbarline[0] != '\0' && strncmp( thinbarline, "no", 2 )!= 0) )
657 		PL_add_legent( LEGEND_LINE, legendlabel, "", thinbarline, "", "" );
658 
659 	else PL_add_legent( LEGEND_COLOR, legendlabel, "", barcolor, "", "" );
660 	}
661 
662 if( baseax == 'y' ) Eflip = 0;
663 
664 return( 0 );
665 }
666 
667 
668 /* ====================== */
669 static int
do_label(s,x,y,align,backbox,reverse)670 do_label( s, x, y, align, backbox, reverse )
671 char *s;
672 double x, y;
673 char align;
674 char *backbox;
675 int reverse;
676 {
677 double halfbox;
678 char tcolor[40];
679 
680 strcpy( tcolor, Enextcolor ); /* remember text color that has been set; backing box could change it below..  scg 3/14/06 */
681 
682 halfbox = ((strlen( s ) * Ecurtextwidth) / 2.0) + 0.01;
683 
684 convertnl( s ); /* added scg 3/8 */
685 
686 
687 if( Eflip ) {
688 	if( reverse && align == 'L' ) {
689 		align = 'R';
690 		/* no backing necessary */
691 		Emov( x-0.02, y );
692 		}
693 	else if( align != 'R' ) {
694 		align = 'L';
695 		/* no backing box necessary, past end of bar */
696 		Emov( x-0.02, y+0.03 );
697 		}
698 	else if( reverse && align == 'R' ) {
699 		align = 'L';
700 		if( backbox[0] != '\0' )
701 			Ecblock( x-0.1, y+0.03,
702 				x+(Ecurtextheight*0.8), y+(halfbox*2)+0.03, backbox, 0 );
703 		Emov( x-0.02, y+0.03 );
704 		}
705 	else 	{ /* R align */
706 		if(  backbox[0] != '\0' )
707 			Ecblock( x-0.1, (y-(halfbox*2))-0.03,
708 				x+(Ecurtextheight*0.8), y-0.03, backbox, 0 );
709 		Emov( x-0.02, y-0.03 );
710 		}
711 	}
712 
713 else 	{
714 	if( backbox[0] != '\0' )
715 		Ecblock( x-halfbox, y-0.01, x+halfbox, y+(Ecurtextheight*0.8), backbox, 0 );
716 	Emov( x, y );
717 	}
718 
719 Ecolor( tcolor ); /* be sure to use text color */
720 
721 Edotext( s, align );
722 
723 return( 0 );
724 }
725 
726 /* ============================ */
727 static int
do_lwl(s,x,y,y0,align,reverse,mustfit)728 do_lwl( s, x, y, y0, align, reverse, mustfit )
729 char *s;
730 double x, y, y0;
731 char align;
732 int reverse;
733 int mustfit;
734 {
735 double y1, y2;
736 int nlines, maxlen;
737 
738 if( y0 < y ) { y1 = y; y2 = y0; }
739 else { y1 = y0; y2 = y; }
740 
741 convertnl( s );
742 measuretext( s, &nlines, &maxlen );
743 
744 
745 if( mustfit == 1 || ( mustfit == 2 && nlines > 1) ) { /* label too long- omit.. added scg 5/11/06 */
746 	if( maxlen*Ecurtextwidth > (y1-y2) ) return( 0 );
747 	}
748 else if( mustfit == 2 && nlines == 1 ) { /* truncate label to fit.. added scg 5/11/06 */
749 	if( maxlen*Ecurtextwidth > (y1-y2) ) {
750 		int nchars;
751 		nchars = (int)( (y1-y2)/ Ecurtextwidth);
752 		if( nchars > 6 ) {
753 			s[ nchars ] = '\0';
754 			s[ nchars-1 ] = '.';
755 			s[ nchars-2 ] = '.';
756 			}
757 		else s[nchars] = '\0';
758 		measuretext( s, &nlines, &maxlen );
759 		}
760 	}
761 
762 if( Eflip ) {
763 	x -= Ecurtextheight*0.4;
764 	x += (((nlines-1)*0.5)*Ecurtextheight);
765 	}
766 else 	{
767 	x += Ecurtextheight*0.4;
768 	x -= (((nlines-1)*0.5)*Ecurtextheight);
769 	}
770 
771 if( reverse ) {
772 	if( align == 'L' ) align = 'R';
773 	else if( align == 'R' ) align = 'L';
774 	}
775 
776 if( align == 'C' ) Emov( x, y2+((y1-y2)/2.0) );
777 else if( align == 'L' ) Emov( x, y2 );
778 else Emov( x, y1 );
779 if( !Eflip ) Etextdir(90+labelrot);
780 else if( labelrot != 0 ) Etextdir( labelrot );
781 Edotext( s, align );
782 /* if( !Eflip ) */
783 Etextdir(0);
784 return( 0 );
785 }
786 
787 /* ================================ */
788 /* RESETSTACKLIST - called when a new areadef is done, or
789    when beginning a new member-of-cluster */
790 
791 int
PL_resetstacklist()792 PL_resetstacklist()
793 {
794 strcpy( stacklist, "" );
795 return( 0 );
796 }
797 
798 
799 /* ======================================================= *
800  * Copyright 1998-2008 Stephen C. Grubb                    *
801  * http://ploticus.sourceforge.net                         *
802  * Covered by GPL; see the file ./Copyright for details.   *
803  * ======================================================= */
804