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