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 LINEPLOT - draw a lineplot */
8
9 #include "pl.h"
10 #define MAXALT 200
11 #define MOVE 0
12 #define LINE 1
13 #define PATH 2
14
15 static int dblcompare( const void *a, const void *b );
16 static int placenum();
17
18 int
PLP_lineplot()19 PLP_lineplot()
20 {
21 char attr[NAMEMAXLEN], *line, *lineval;
22 int lvp, first;
23
24 char buf[256], numstr[40], symcode[80];
25 int i, j, k, stat, align, accum;
26 int npoints, result, nalt, altlist[MAXALT+2], anyvalid, realrow, sortopt;
27 int yfield, xfield, ptlabelfield;
28 int stairstep, donumbers, dopoints, instancemode, gapmissing, ingap, clipping, firstpt, groupmode, relax_xrange, fillmode;
29
30 char *linedetails, *label, *labeldetails, *shownums, *numstrfmt, *pointsym, *ptlabeldetails, *fillcolor;
31 char *legendlabel, *selectex, *forcelastx, *legsamptyp, *altsym, *altwhen;
32
33 double adjx, adjy, linestart, linestop, x, y, lastseglen, lastx, lasty, radius, ptlblstart, ptlblstop, sob, linxstart;
34 double f, sum, cr, firstx, firsty, typical_interval;
35 double fillbleed;
36
37 TDH_errprog( "pl proc lineplot" );
38
39 /* initialize */
40 yfield = -1; xfield = -1;
41 ptlabelfield = 0;
42 accum = 0; stairstep = 0; fillmode = 0; instancemode = 0; groupmode = 0;
43 gapmissing = 0; clipping = 0; firstpt = 0; sortopt = 0; relax_xrange = 0;
44 label = ""; labeldetails = ""; shownums = ""; ptlabeldetails = ""; linedetails = ""; pointsym = "";
45 legendlabel = ""; selectex = ""; forcelastx = ""; altsym = ""; altwhen = "";
46 numstrfmt = "%g";
47 fillcolor = "gray(0.8)";
48 legsamptyp = "symbol";
49 linestart = EDXlo; linestop = EDXhi;
50 ptlblstart = EDXlo; ptlblstop = EDXhi;
51 linxstart = EDXlo;
52 sob = 0.0;
53 lastseglen = 0.0;
54 typical_interval = -99.0;
55 fillbleed = 0.0;
56
57
58
59 /* get attributes.. */
60 first = 1;
61 while( 1 ) {
62 line = getnextattr( first, attr, &lvp );
63 if( line == NULL ) break;
64 first = 0;
65 lineval = &line[lvp];
66
67 if( strcmp( attr, "yfield" )==0 ) yfield = fref( lineval ) - 1;
68 else if( strcmp( attr, "xfield" )==0 ) xfield = fref( lineval ) - 1;
69 else if( strcmp( attr, "ptlabelfield" )==0 ) ptlabelfield = fref( lineval ) - 1;
70 else if( strcmp( attr, "linedetails" )==0 ) linedetails = lineval;
71 else if( strcmp( attr, "label" )==0 ) { label = lineval; convertnl( label ); }
72 else if( strcmp( attr, "labeldetails" )==0 ) labeldetails = lineval;
73 else if( strcmp( attr, "legendlabel" )==0 ) legendlabel = lineval;
74 else if( strcmp( attr, "linerange" )==0 ) getrange( lineval, &linestart, &linestop, 'x', EDXlo, EDXhi );
75 else if( strcmp( attr, "xstart" )==0 ) { linxstart = Econv( X, lineval ); if( Econv_error() ) linxstart = EDXlo; }
76 else if( strcmp( attr, "firstpoint" )==0 ) {
77 char xstr[80], ystr[80];
78 sscanf( lineval, "%s %s", xstr, ystr );
79 firstpt = 1;
80 firstx = Econv( X, xstr); if( Econv_error() ) firstpt = 0;
81 firsty = Econv( X, ystr ); if( Econv_error() ) firstpt = 0;
82 }
83 else if( strcmp( attr, "accum" )==0 ) accum = getyn( lineval );
84 else if( strcmp( attr, "stairstep" )==0 ) stairstep = getyn( lineval );
85 else if( strcmp( attr, "instancemode" )==0 ) instancemode = getyn( lineval );
86 else if( strcmp( attr, "groupmode" )==0 ) groupmode = getyn( lineval );
87 else if( strcmp( attr, "gapmissing" )==0 ) {
88 if( strncmp( lineval, "y", 1 )==0 ) gapmissing = 1;
89 else if( strcmp( lineval, "small" )==0 ) gapmissing = 2;
90 else if( strcmp( lineval, "auto" )==0 ) gapmissing = 3;
91 else if( strcmp( lineval, "autosmall" )==0 ) gapmissing = 4;
92 else if( strcmp( lineval, "autozero" )==0 ) gapmissing = 5;
93 else gapmissing = 0;
94 }
95 else if( strcmp( attr, "clip" )==0 ) clipping = getyn( lineval );
96 else if( strcmp( attr, "sort" )==0 ) sortopt = getyn( lineval );
97 else if( strcmp( attr, "relax_xrange" ) == 0 ) relax_xrange = getyn( lineval );
98 else if( strcmp( attr, "lastseglen" )==0 ) Elenex( lineval, X, &lastseglen );
99 else if( strcmp( attr, "numbers" )==0 ) shownums = lineval;
100 else if( strcmp( attr, "numbersformat" )==0 ) numstrfmt = lineval;
101 else if( strcmp( attr, "select" )==0 ) selectex = lineval;
102 else if( strcmp( attr, "altsymbol" )==0 ) altsym = lineval;
103 else if( strcmp( attr, "altwhen" )==0 ) altwhen = lineval;
104 else if( strcmp( attr, "lastx" )==0 ) forcelastx = lineval;
105 else if( strcmp( attr, "pointsymbol" )==0 ) pointsym = lineval;
106 else if( strcmp( attr, "ptlabeldetails" )==0 ) ptlabeldetails = lineval;
107 else if( strcmp( attr, "ptlabelrange" )==0 ) {
108 if( strcmp( lineval, "last" )==0 ) ptlblstart = EDXhi-0.1;
109 else if( strcmp( lineval, "first" )==0 ) { ptlblstart = EDXlo; ptlblstop = EDXhi-0.000001; }
110 else getrange( lineval, &ptlblstart, &ptlblstop, 'x', EDXlo, EDXhi );
111 }
112 else if( strcmp( attr, "stairoverbars" )==0 ) {
113 if( strncmp( lineval, "y", 1 )==0 ) { sob = 0.5; stairstep = 1; /* implied */ }
114 else sob = 0.0;
115 }
116 else if( strcmp( attr, "fill" )==0 ) {
117 fillcolor = lineval;
118 if( fillcolor[0] != '\0' ) fillmode = 1;
119 else fillmode = 0;
120 }
121 else if( strcmp( attr, "fillbleed" )==0 ) fillbleed = ftokncpy( lineval );
122 else if( strcmp( attr, "legendsampletype" )==0 ) legsamptyp = lineval;
123 else Eerr( 1, "attribute not recognized", attr );
124 }
125
126
127 /* overrides and degenerate cases */
128 /* -------------------------- */
129 if( Nrecords < 1 ) return( Eerr( 17, "No data has been read yet w/ proc getdata", "" ) );
130 if( !scalebeenset() )
131 return( Eerr( 51, "No scaled plotting area has been defined yet w/ proc areadef", "" ) );
132
133
134 if( (yfield < 0 || yfield >= Nfields ) && !instancemode ) return( Eerr( 601, "yfield out of range", "" ) );
135 if( xfield >= Nfields ) return( Eerr( 601, "xfield out of range", "" ) );
136 if( yfield >= 0 && instancemode ) {
137 Eerr( 4729, "warning, turning instancemode off since yfield specified", "" );
138 instancemode = 0;
139 }
140
141 if( groupmode && ptlabelfield ) {
142 Eerr( 4729, "warning, turning ptlabelfield off since groupmode specified", "" );
143 ptlabelfield = 0;
144 }
145
146 if( strncmp( legendlabel, "#usefname", 9 )==0 ) {
147 if( instancemode ) getfname( xfield+1, legendlabel );
148 else getfname( yfield+1, legendlabel );
149 }
150
151
152
153 /* now do the plotting work.. */
154 /* -------------------------- */
155
156 donumbers = 1;
157 if( GL_slmember( shownums, "no*" ) || shownums[0] == '\0' ) donumbers = 0;
158
159 dopoints = 1;
160 if( GL_slmember( pointsym, "no*" ) || pointsym[0] == '\0' ) dopoints = 0;
161
162
163 /* put all values into PLV vector, doing accumulation if required.. */
164 j = 0;
165 f = linxstart;
166 nalt = 0;
167
168 for( i = 0; i < Nrecords; i++ ) {
169
170 if( selectex[0] != '\0' ) { /* process against selection condition if any.. */
171 stat = do_select( selectex, i, &result );
172 if( stat != 0 ) { Eerr( stat, "Select error", selectex ); continue; }
173 if( result == 0 ) continue; /* reject */
174 }
175 if( altwhen[0] != '\0' ) { /* check altwhen condition.. */
176 stat = do_select( altwhen, i, &result );
177 if( stat != 0 ) { Eerr( stat, "Select error", altwhen ); continue; }
178 if( result == 1 && nalt < MAXALT ) {
179 /* altlist[nalt] = j/2; */
180 altlist[nalt] = j/3;
181 nalt++;
182 }
183 }
184
185
186
187 /* X */
188 if( xfield < 0 ) {
189 PLV[j] = f + sob;
190 f += 1.0;
191 }
192 else {
193 PLV[j] = fda( i, xfield, X ) + sob;
194 if( Econv_error() ) {
195 conv_msg( i, xfield, "xfield" );
196 continue;
197 }
198 }
199
200 j++;
201
202 /* Y */
203 if( instancemode ) PLV[j] = 1.0;
204 else {
205 PLV[j] = fda( i, yfield, Y );
206 if( Econv_error() ) {
207 conv_msg( i, yfield, "yfield" );
208 PLV[j] = NEGHUGE;
209 /* continue; removed scg 5/19/99 */
210 }
211 }
212
213 j++;
214
215 PLV[j] = (double)i;
216 j++;
217
218 if( j >= PLVsize-1 ) {
219 Eerr( 3579, "Sorry, too many curve points, curve truncated (raise using -maxvector)", "" );
220 break;
221 }
222 }
223
224 npoints = j / 3;
225
226
227 /* sort if required.. */ /* added 4/22/02 */
228 if( sortopt ) {
229 if( PLS.debug ) fprintf( PLS.diagfp, "sorting points for line\n" );
230 qsort( PLV, npoints, sizeof(double)*3, dblcompare );
231 }
232
233
234
235 /* process for groupmode.. */
236 if( groupmode && xfield >= 0 ) for( i = 0; i < npoints; i++ ) {
237
238 for( k = i+1; k < npoints; k++ ) {
239
240 if( dat3d(i,0) == dat3d(k,0) ) {
241 if( instancemode ) y = 1.0;
242 else y = dat3d( k, 1 );
243 dat3d( k, 1 ) = NEGHUGE; /* rub out the additional instance.. */
244 if( y > NEGHUGE+1 ) (dat3d( i, 1 )) += (y);
245 }
246 else {
247 i = k-1; /* back off.. */
248 break;
249 }
250 }
251 }
252 /* fprintf( stderr, "after grouping\n" );
253 * for( i = 0; i < npoints; i++ ) fprintf( stderr, "%g %g %g\n", dat3d(i,0), dat3d(i,1), dat3d(i,2 ) );
254 */
255
256
257 /* process for accum.. */
258 if( accum ) {
259 sum = 0.0;
260 for( i = 0; i < npoints; i++ ) {
261
262 if( dat3d( i, 1 ) > (NEGHUGE+1) ) {
263 sum += (dat3d( i, 1 ));
264 (dat3d( i, 1 )) = sum;
265 }
266 }
267 }
268
269 /* fprintf( stderr, "after accum\n" );
270 * for( i = 0; i < npoints; i++ ) fprintf( stderr, "%g %g %g\n", dat3d(i,0), dat3d(i,1), dat3d(i,2 ) );
271 */
272
273
274
275 /* draw the curve.. */
276 /* ---------------- */
277
278 /* set line parameters */
279 linedet( "linedetails", linedetails, 1.0 );
280
281 if( fillmode ) {
282 Ecolor( fillcolor ); /* scg 6/18/04 */
283 }
284
285
286 first = 1;
287 lasty = 0.0;
288 lastx = 0.0;
289 anyvalid = 0;
290 ingap = 0;
291 cr = Elimit( Y, 'l', 's' );
292 for( i = 0; i < npoints; i++ ) {
293 if( !first && (y > (NEGHUGE+1) && x > (NEGHUGE+1) ) ) { lasty = y; lastx = x; }
294
295 if( first && firstpt ) {
296 x = firstx;
297 y = firsty;
298 }
299 else {
300 x = dat3d(i,0);
301 y = dat3d(i,1);
302 }
303
304 if( x < (NEGHUGE+1) || y < (NEGHUGE+1) ) {
305 if( gapmissing ) ingap = 1;
306 continue; /* skip bad values */
307 }
308
309 if( x < linestart && !relax_xrange ) continue; /* X out of range - lo */
310 if( x > linestop && !relax_xrange ) { /* X out of range - hi */
311 x = lastx; /* back up to last in-range point so last stairtstep is correct*/
312 y = lasty;
313 break;
314 }
315 if( !first && ( gapmissing == 3 || gapmissing == 4 || gapmissing == 5 )) {
316 if( typical_interval < 0.0 ) typical_interval = (x - lastx)*1.01;
317 else if( x - lastx > typical_interval ) {
318 /* if( gapmissing == 5 ) { Elinu( lastx+typical_interval, 0.0 ); Elinu( x-typical_interval, 0.0 ); } */
319 if( gapmissing == 5 ) { Elinu( lastx, 0.0 ); Elinu( x, 0.0 ); Elinu( x, y ); } /* changed, scg 11/20/07 */
320 else ingap = 1;
321 }
322 }
323
324 if( !anyvalid && !Ef_inr( Y, y ) ) continue; /* 1/9/03 scg - anyvalid should not become 1 if out of range in Y */
325
326 anyvalid = 1;
327 if( first ) {
328 Emovu( x, y );
329 setfloatvar( "XSTART", x, "%g" );
330 setfloatvar( "YSTART", y, "%g" );
331 first = 0;
332 ingap = 0;
333 continue;
334 }
335 if( !first && fillmode && !ingap ) {
336 Emovu( x, cr );
337 Epath( Eax( lastx )-fillbleed, Eay( cr ) ); /* -0.005 added to remove tiny gap artifact seen on EPS ... scg 1/9/08 */
338 Epath( Eax( lastx )-fillbleed, Eay( lasty ) );
339 /* was: Epathu( lastx, cr ); */
340 /* Epathu( lastx, lasty ); */
341
342 if( stairstep ) Epathu( x, lasty );
343 else Epathu( x, y );
344 Ecolor( fillcolor ); /* added scg 11/2/07 - color bug related to gapmissing=2 and fill=1 */
345 Efill();
346 }
347 if( ( gapmissing == 2 || gapmissing ==4 ) && ingap ) { /* do a quarter-length nib at previous location */
348 double nib;
349 Emovu( lastx, lasty );
350 nib = (x-lastx) * 0.25;
351 if( fillmode ) Ecblock( Eax(lastx), Eay(cr), Eax(lastx+nib), Eay(lasty), fillcolor, 0 );
352 else Elinu( lastx+nib, lasty );
353 }
354 if( ! fillmode ) {
355 if( ingap ) Emovu( x, y );
356 if( stairstep && x > linestart && !ingap ) Elinu( x, lasty );
357 if( stairstep && x == linestart ) Emovu( x, y );
358 if( clipping && !ingap && !stairstep ) {
359 double cx1, cy1, cx2, cy2;
360 cx1 = lastx; cy1 = lasty; cx2 = x; cy2 = y;
361 stat = Elineclip( &cx1, &cy1, &cx2, &cy2, EDXlo, EDYlo, EDXhi, EDYhi );
362 if( !stat ) { Emovu( cx1, cy1 ); Elinu( cx2, cy2 ); }
363 }
364 else Elinu( x, y );
365 }
366 ingap = 0;
367 }
368
369 if( !anyvalid ) { /* no plottable data points.. exit */
370 /* Ecolor( oldcolor ); */ /* don't do color chg - scg 5/10/05 */
371 return( 0 );
372 }
373
374 /* if last point was invalid, back up to most recent valid point.. */
375 if( x < (NEGHUGE+1) || y < (NEGHUGE+1) ) { x = lastx; y = lasty; }
376
377
378 /* handle last segment of stairstep.. */
379 /* if( stairstep ) { */ /* } changed to allow lastseglen to be used anytime, scg 12/13/01 */
380 if( lastseglen > 0.0 ) {
381 if( x < (NEGHUGE+1) || y < (NEGHUGE+1) ) { x = lastx; y = lasty; }
382 lastx = Eax( x ) + lastseglen;
383 if( fillmode ) {
384 Emov( Eax(x), Eay(cr) ); Epath( lastx, Eay(cr) );
385 Epath( lastx, Eay(y) ); Epath( Eax(x), Eay(y) );
386 /* Ecolorfill( fillcolor ); */ /* using Efill .. scg 6/18/04 */
387 Efill();
388 }
389 else Elin( lastx, Eay( y ) );
390 }
391 else lastx = Eax(x);
392
393 if( forcelastx[0] != '\0' ) {
394 lastx = Eax( Econv( X, forcelastx ) );
395 if( !Econv_error() && anyvalid ) Elin( lastx, Eay( y ) );
396 }
397
398
399 /* set YFINAL and XFINAL */
400 Euprint( numstr, Y, y, numstrfmt );
401 setcharvar( "YFINAL", numstr );
402 Euprint( numstr, X, lastx, numstrfmt );
403 setcharvar( "XFINAL", numstr );
404
405
406
407
408
409
410 /* do points, labels, etc. */
411 /* ----------------------- */
412 for( i = 0; i < npoints; i++ ) {
413 x = dat3d(i,0);
414 y = dat3d(i,1);
415
416
417 if( x < (NEGHUGE+1) || y < (NEGHUGE+1) ) continue; /* skip bad values */
418
419 if( x < linestart && !relax_xrange ) continue; /* out of range - lo */
420 if( x > linestop && !relax_xrange ) { /* out of range - hi */
421 break;
422 }
423
424 if( clipping && !stairstep && !fillmode ) {
425 /* if clipping, suppress points or labels that are outside the plotting area */
426 if( x < EDXlo || x > EDXhi || y < EDYlo || y > EDYhi ) continue;
427 }
428
429 lasty = y;
430
431 if( x >= ptlblstart && x <= ptlblstop ) {
432 if( donumbers && stairstep ) {
433 if( i == npoints-1 || GL_close_to( x, linestop, 0.01 ) ) {
434 double xls;
435 xls = Edx(lastseglen) - Edx(0.0);
436 placenum( shownums, x, x + xls, y, numstrfmt, linedetails );
437 }
438 else placenum( shownums, x, dat3d(i+1,0), y, numstrfmt, linedetails );
439 }
440 else if( donumbers && !stairstep )
441 placenum( shownums, x, x, y, numstrfmt, linedetails );
442 if( donumbers && fillmode ) Ecolor( fillcolor ); /* scg 6/18/04 */
443 }
444
445 if( dopoints ) {
446 int jj;
447 /* see if this is one of the alternates.. if so use altsym rather
448 than the regular symbol */
449 for( jj = 0; jj < nalt; jj++ ) if( i == altlist[jj] ) break;
450 if( jj != nalt ) symdet( "altsym", altsym, symcode, &radius );
451 else symdet( "pointsymbol", pointsym, symcode, &radius );
452 Emark( Eax(x), Eay(y), symcode, radius );
453 }
454 realrow = (int) dat3d(i,2);
455 if( ptlabelfield && x >= ptlblstart && x <= ptlblstop ) {
456 textdet( "ptlabeldetails", ptlabeldetails, &align, &adjx, &adjy, -4, "R", 1.0 );
457 if( align == '?' ) align = 'C';
458 Emov( Eax( x ) + adjx, Eay( y ) + adjy );
459 Edotext( da( realrow, ptlabelfield ), align );
460 }
461 }
462
463
464 if( label[0] != '\0' ) {
465 GL_varsub( label, "@YFINAL", numstr );
466 PL_do_subst( buf, label, realrow, 0 ); /* also allow substitution of any @field on the last-plotted data row.. added scg 1/29/07 */
467 textdet( "labeldetails", labeldetails, &align, &adjx, &adjy, -2, "R", 1.0 );
468 if( align == '?' ) align = 'L';
469 Emov( lastx+0.05+adjx, (Eay( lasty )-(Ecurtextheight*.35))+adjy );
470 Edotext( buf, align );
471 }
472
473 if( legendlabel[0] != '\0' ) {
474 if( fillmode ) PL_add_legent( LEGEND_COLOR, legendlabel, "", fillcolor, "", "" );
475 else if( pointsym[0] != '\0' && strcmp( pointsym, "none" )!= 0 ) {
476 if( legsamptyp[0] == 's' && strlen( legsamptyp ) <= 6 )
477 PL_add_legent( LEGEND_SYMBOL, legendlabel, "", pointsym, "", "" );
478 else
479 PL_add_legent( LEGEND_LINE+LEGEND_SYMBOL, legendlabel, "", linedetails, pointsym, "" );
480 }
481 else PL_add_legent( LEGEND_LINE, legendlabel, "", linedetails, "", "" );
482 }
483
484 /* if( fillmode ) Ecolor( oldcolor ); */ /* restore */
485 return( 0 );
486 }
487
488 /* ----------------------- */
489 /* place one line plot number */
490 static int
placenum(shownums,x1,x2,y,numstrfmt,linedetails)491 placenum( shownums, x1, x2, y, numstrfmt, linedetails )
492 char *shownums;
493 double x1, x2, y;
494 char *numstrfmt, *linedetails;
495 {
496 char numstr[20];
497 int align;
498 double adjx, adjy;
499
500 /* change to text color, size, etc.. */
501 textdet( "numbers", shownums, &align, &adjx, &adjy, -4, "R", 1.0 );
502 if( align == '?' ) align = 'C';
503 Emov( Eax( (x1+x2)/2.0 ) + adjx, Eay(y)+0.02+adjy );
504 /* sprintf( numstr, numstrfmt, y ); */
505 Euprint( numstr, 'y', y, numstrfmt );
506 Edotext( numstr, align );
507 linedet( "linedetails", linedetails, 1.0 );
508 Emovu( x1, y ); /* restore old position for drawing the curve.. */
509 return( 0 );
510 }
511
512 /* ------------------------- */
513 static int
dblcompare(a,b)514 dblcompare( a, b )
515 const void *a, *b;
516 {
517 double *f, *g;
518 f = (double *)a;
519 g = (double *)b;
520
521 if( *f > *g ) return( 1 );
522 if( *f < *g ) return( -1 );
523 return( 0 );
524 }
525
526
527 /* ======================================================= *
528 * Copyright 1998-2005 Stephen C. Grubb *
529 * http://ploticus.sourceforge.net *
530 * Covered by GPL; see the file ./Copyright for details. *
531 * ======================================================= */
532