1 /*
2  * $Id: draw.c,v 1.1 2005-09-18 22:04:28 dhmunro Exp $
3  * Implement display list portion of GIST C interface
4  */
5 /* Copyright (c) 2005, The Regents of the University of California.
6  * All rights reserved.
7  * This file is part of yorick (http://yorick.sourceforge.net).
8  * Read the accompanying LICENSE file for details.
9  */
10 
11 #include "draw.h"
12 #include "gtext.h"
13 #include "pstdlib.h"
14 
15 /* Generating default contour labels requires sprintf function */
16 #include <stdio.h>
17 
18 #include <string.h>
19 
20 extern double log10(double);
21 #define SAFELOG0 (-999.)
22 #define SAFELOG(x) ((x)>0? log10(x) : ((x)<0? log10(-(x)) : -999.))
23 
24 /* In tick.c */
25 extern GpReal GpNiceUnit(GpReal finest, int *base, int *power);
26 
27 extern double floor(double);
28 extern double ceil(double);
29 #ifndef NO_EXP10
30   extern double exp10(double);
31 #else
32 # define exp10(x) pow(10.,x)
33   extern double pow(double,double);
34 #endif
35 #define LOG2 0.301029996
36 
37 /* ------------------------------------------------------------------------ */
38 
39 static Drauing *currentDr= 0;  /* Drauing from GdNewDrawing or GdGetDrawing */
40 static GeSystem *currentSy;    /* System from GdNewSystem or GdSetSystem */
41 static GdElement *currentEl;   /* Element extracted with GdSetSystem, or
42                                   GdGetElement */
43 static int currentCn;          /* level selected by GdGetContour */
44 
45 /* Saved state information used by GdSetDrawing */
46 static Drauing *saveDr= 0;
47 static GeSystem *saveSy;
48 static GdElement *saveEl;
49 static int saveCn;
50 
51 GdProperties gistD= {
52   0, 0,
53   {
54   {7.5, 50., 1.2, 1.2, 4, 1, TICK_L|TICK_U|TICK_OUT|LABEL_L,
55    0.0, 14.0*ONE_POINT,
56    {12.*ONE_POINT, 8.*ONE_POINT, 5.*ONE_POINT, 3.*ONE_POINT, 2.*ONE_POINT},
57      {FG_COLOR, L_SOLID, 1.0},
58      {FG_COLOR, L_DOT, 1.0},
59      {FG_COLOR, T_HELVETICA, 14.*ONE_POINT,
60         TX_RIGHT, TH_NORMAL, TV_NORMAL, 1},
61      .425, .5-52.*ONE_POINT},
62   {7.5, 50., 1.2, 1.2, 3, 1, TICK_L|TICK_U|TICK_OUT|LABEL_L,
63    0.0, 14.0*ONE_POINT,
64    {12.*ONE_POINT, 8.*ONE_POINT, 5.*ONE_POINT, 3.*ONE_POINT, 2.*ONE_POINT},
65      {FG_COLOR, L_SOLID, 1.0},
66      {FG_COLOR, L_DOT, 1.0},
67      {FG_COLOR, T_HELVETICA, 14.*ONE_POINT,
68         TX_RIGHT, TH_NORMAL, TV_NORMAL, 1},
69      .25, .5-52.*ONE_POINT},
70   0, {FG_COLOR, L_SOLID, 1.0}
71   },
72   {{ 0.0, 1.0, 0.0, 1.0 }, { 0.0, 1.0, 0.0, 1.0 }},
73   D_XMIN | D_XMAX | D_YMIN | D_YMAX,    /* flags */
74   { 0.0, 1.0, 0.0, 1.0 },               /* limits */
75   0, 0, 0, 0, 0,
76   0.0, 0.0, 0,
77   0.0, 0.0, 0.0, 0.0, 0, 0,             /* GdCells */
78   0, 0,
79   0, { 0, 0, 0, 0, 0, 0 }, 0,           /* noCopy, mesh, region */
80   0, 0,
81   0, 0, 0.0,
82   0, 0, 0,  0 };
83 
84 Drauing *gistDrawList= 0;
85 
86 static void ClearDrawing(Drauing *drawing);
87 static void Damage(GeSystem *sys, GdElement *el);
88 static void SquareAdjust(GpReal *umin, GpReal *umax,
89                          GpReal dv, int doMin, int doMax);
90 static void NiceAdjust(GpReal *umin, GpReal *umax, int isLog, int isMin);
91 static void EqAdjust(GpReal *umin, GpReal *umax);
92 static void EmptyAdjust(GpReal *umin, GpReal *umax, int doMin, int doMax);
93 static void EqualAdjust(GpReal *umin, GpReal *umax, int doMin, int doMax);
94 extern int Gd_DrawRing(void *elv, int xIsLog, int yIsLog,
95                        GeSystem *sys, int t);
96 static void InitLegends(int contours, GeSystem *systems, GdElement *elements,
97                         int size);
98 static void NextContours(void);
99 static int NextRing(void);
100 static int NextLegend(void);
101 static int BuildLegends(int more, int contours, GeSystem *systems,
102                         GdElement *elements, GeLegendBox *lbox);
103 static int MemoryError(void);
104 static void *Copy1(const void *orig, long size);
105 static void *Copy2(void *x1, const void *orig1, const void *orig2, long size);
106 extern void Gd_ScanZ(long n, const GpReal *z, GpReal *zmin, GpReal *zmax);
107 static void ScanXY(long n, const GpReal *x, const GpReal *y, GpBox *extrema);
108 
109 extern void Gd_NextMeshBlock(long *ii, long *jj, long len, long iMax,
110                              int *reg, int region);
111 extern void Gd_MeshXYGet(void *vMeshEl);
112 static int AutoMarker(GaLineAttribs *dl, int number);
113 extern int Gd_MakeContours(GeContours *con);
114 static void GuessBox(GpBox *box, GpBox *viewport, GaTickStyle *ticks);
115 static GdElement *NextConCurve(GdElement *el);
116 static int GeFindIndex(int id, GeSystem *sys);
117 
118 extern void Gd_KillRing(void *elv);
119 extern void Gd_KillMeshXY(void *vMeshEl);
120 
121 static void (*DisjointKill)(void *el);
122 static void (*FilledKill)(void *el);
123 static void (*VectorsKill)(void *el);
124 static void (*ContoursKill)(void *el);
125 static void (*SystemKill)(void *el);
126 static int (*LinesGet)(void *el);
127 static int (*ContoursGet)(void *el);
128 extern void Gd_LinesSubSet(void *el);
129 static int (*SystemDraw)(void *el, int xIsLog, int yIsLog);
130 
131 /* ------------------------------------------------------------------------ */
132 /* Set virtual function tables */
133 
134 extern GdOpTable *GetDrawingOpTables(void);  /* in draw0.c */
135 static GdOpTable *opTables= 0;
136 
137 /* ------------------------------------------------------------------------ */
138 /* Constructor and destructor for Drauing declared in gist.h */
139 
GdNewDrawing(char * gsFile)140 Drauing *GdNewDrawing(char *gsFile)
141 {
142   Drauing *drawing= p_malloc(sizeof(Drauing));
143   if (!drawing) return 0;
144   if (!opTables) {
145     opTables= GetDrawingOpTables();
146     DisjointKill= opTables[E_DISJOINT].Kill;
147     FilledKill= opTables[E_FILLED].Kill;
148     VectorsKill= opTables[E_VECTORS].Kill;
149     ContoursKill= opTables[E_CONTOURS].Kill;
150     SystemKill= opTables[E_SYSTEM].Kill;
151     LinesGet= opTables[E_LINES].GetProps;
152     ContoursGet= opTables[E_CONTOURS].GetProps;
153     SystemDraw= opTables[E_SYSTEM].Draw;
154   }
155 
156   drawing->next= gistDrawList;
157   gistDrawList= drawing;
158   drawing->cleared=
159     drawing->nSystems= drawing->nElements= 0;
160   drawing->systems= 0;
161   drawing->elements= 0;
162   drawing->damaged= 0;
163   drawing->damage.xmin= drawing->damage.xmax=
164     drawing->damage.ymin= drawing->damage.ymax= 0.0;
165   drawing->landscape= 0;
166   drawing->legends[0].nlines= drawing->legends[1].nlines= 0;
167 
168   GdSetDrawing(drawing);
169 
170   if (GdReadStyle(drawing, gsFile)) {
171     GdSetDrawing(0);
172     GdKillDrawing(drawing);
173     return 0;
174   }
175 
176   return drawing;
177 }
178 
GdLandscape(int landscape)179 int GdLandscape(int landscape)
180 {
181   if (!currentDr) return 1;
182   if (landscape) landscape= 1;
183   if (currentDr->landscape!=landscape) {
184     currentDr->landscape= landscape;
185     GdDetach(currentDr, 0);
186   }
187   return 0;
188 }
189 
GdKillDrawing(Drauing * drawing)190 void GdKillDrawing(Drauing *drawing)
191 {
192   if (!drawing) {
193     drawing= currentDr;
194     if (!drawing) return;
195   }
196 
197   ClearDrawing(drawing);
198   Gd_KillRing(drawing->systems);
199 
200   if (drawing==gistDrawList) gistDrawList= drawing->next;
201   else {
202     Drauing *draw= gistDrawList;
203     while (draw->next!=drawing) draw= draw->next;
204     draw->next= drawing->next;
205   }
206 
207   if (drawing==currentDr) currentDr= 0;
208 
209   p_free(drawing);
210 }
211 
212 extern void GdKillSystems(void);
213 
GdKillSystems(void)214 void GdKillSystems(void)
215 {
216   if (!currentDr) return;
217   ClearDrawing(currentDr);
218   Gd_KillRing(currentDr->systems);
219   currentDr->systems= 0;
220   currentDr->nSystems= 0;
221 }
222 
GdSetDrawing(Drauing * drawing)223 int GdSetDrawing(Drauing *drawing)
224 {
225   int nMax, sysIndex, i;
226   GeSystem *sys;
227 
228   if (!drawing) {  /* swap current and saved state info */
229     Drauing *tmpDr= currentDr;
230     GeSystem *tmpSy= currentSy;
231     GdElement *tmpEl= currentEl;
232     int tmpCn= currentCn;
233     currentDr= saveDr;   saveDr= tmpDr;
234     currentSy= saveSy;   saveSy= tmpSy;
235     currentEl= saveEl;   saveEl= tmpEl;
236     currentCn= saveCn;   saveCn= tmpCn;
237     return 0;
238   }
239 
240   saveDr= currentDr;
241   saveSy= currentSy;
242   saveEl= currentEl;
243   saveCn= currentCn;
244 
245   currentDr= drawing;
246 
247   /* Make a reasonable guess at current system and element */
248   nMax= drawing->elements? drawing->elements->prev->number : -1;
249   sysIndex= drawing->nSystems? 1 : 0;
250   i= 0;
251   if ((sys= drawing->systems)) do {
252     i++;
253     if (sys->el.number>nMax) { nMax= sys->el.number;  sysIndex= i; }
254     sys= (GeSystem *)sys->el.next;
255   } while (sys!=drawing->systems);
256   GdSetSystem(sysIndex);
257   if (nMax<0) {
258     if (sysIndex<1) currentSy= 0;
259     currentEl= 0;
260   } else {
261     GdElement *el= currentSy? currentSy->elements : drawing->elements;
262     if (el) {
263       currentEl= el->prev;
264       currentEl->ops->GetProps(currentEl);
265     } else {
266       currentEl= 0;
267     }
268   }
269   currentCn= -1;
270 
271   return 0;
272 }
273 
GdClear(Drauing * drawing)274 int GdClear(Drauing *drawing)
275 {
276   if (!drawing) drawing= currentDr;
277   if (!drawing) return 1;
278   drawing->cleared= 1;
279   return 0;
280 }
281 
ClearDrawing(Drauing * drawing)282 static void ClearDrawing(Drauing *drawing)
283 {
284   GeSystem *sys, *sys0= drawing->systems;
285   int nSystems= 0;
286   if ((sys= sys0)) do {
287     Gd_KillRing(sys->elements);
288     sys->elements= 0;
289     sys->rescan= 0;
290     sys->unscanned= -1;
291     sys->el.number= -1;
292     sys= (GeSystem *)sys->el.next;
293     nSystems++;
294   } while (sys!=sys0);
295   Gd_KillRing(drawing->elements);
296   drawing->elements= 0;
297   drawing->nElements= 0;
298   drawing->nSystems= nSystems;
299   drawing->cleared= 2;
300 
301   if (drawing==currentDr) {
302     currentSy= drawing->systems; /* as after GdSetDrawing */
303     currentEl= 0;
304     currentCn= -1;
305   }
306 
307   /* Must detatch drawing from all engines, since even inactive ones
308      need to know that the drawing has been erased.  */
309   GdDetach(drawing, (Engine *)0);
310 }
311 
312 /* ------------------------------------------------------------------------ */
313 
314 /* The GIST display list is called a "drawing".  Any number of drawings
315    may exist, but only one may be active at a time.
316 
317    The entire drawing is rendered on all active engines by calling
318    GdDraw(0).  The drawing sequence is preceded by GpClear(0, CONDITIONALLY).
319 
320    GdDraw(-1) is like GdDraw(0) except the drawing is damaged to force
321    all data to be rescanned as well.
322 
323    GdDraw(1) draws only the changes since the previous GdDraw.  Changes can
324    be either destructive or non-destructive:
325 
326      If the engine->damage box is set, then some operation has
327      damaged that part of the drawing since the prior GdDraw
328      (such as changing a line style or plot limits).  The damaged
329      section is cleared, then the display list is traversed, redrawing
330      all elements which may intersect the damaged box, clipped to
331      the damaged box.
332 
333      Then, any elements which were added since the prior GdDraw
334      (and which caused no damage like a change in limits) are
335      drawn as non-destructive updates.
336 
337  */
338 
Damage(GeSystem * sys,GdElement * el)339 static void Damage(GeSystem *sys, GdElement *el)
340 {
341   GpBox *box, adjustBox;
342   if (!currentDr) return;
343   if (!el) {
344     if (!sys) return;
345     /* If no element, damage the entire coordinate system, ticks and all */
346     box= &sys->el.box;
347   } else if (sys) {
348     /* If element is in a coordinate system, damage the whole viewport */
349     box= &sys->trans.viewport;
350   } else {
351     /* Elements not in a coordinate system already have NDC box--
352        but may need to adjust it to allow for projecting junk.  */
353     el->ops->Margin(el, &adjustBox);
354     adjustBox.xmin+= el->box.xmin;
355     adjustBox.xmax+= el->box.xmax;
356     adjustBox.ymin+= el->box.ymin;
357     adjustBox.ymax+= el->box.ymax;
358     box= &adjustBox;
359   }
360   if (currentDr->damaged) {
361     GpSwallow(&currentDr->damage, box);
362   } else {
363     currentDr->damage= *box;
364     currentDr->damaged= 1;
365   }
366 }
367 
SquareAdjust(GpReal * umin,GpReal * umax,GpReal dv,int doMin,int doMax)368 static void SquareAdjust(GpReal *umin, GpReal *umax,
369                          GpReal dv, int doMin, int doMax)
370 {
371   if (doMin) {
372     if (doMax) umax[0]= 0.5*(umin[0]+umax[0]+dv);
373     umin[0]= umax[0]-dv;
374   } else if (doMax) {
375     umax[0]= umin[0]+dv;
376   }
377 }
378 
NiceAdjust(GpReal * umin,GpReal * umax,int isLog,int isMin)379 static void NiceAdjust(GpReal *umin, GpReal *umax, int isLog, int isMin)
380 {
381   GpReal un= *umin,   ux= *umax;
382   GpReal unit,   du= ux-un;
383   int base, power, reverted= 0;
384   if (isLog) {
385     if (du <= LOG2) {
386       /* revert to linear scale */
387       un= exp10(un);
388       ux= exp10(ux);
389       du= ux-un;
390       reverted= 1;
391     } else if (du>6.0 && isMin) {
392       un= ux-6.0;
393       du= 6.0;
394     }
395   }
396   unit= GpNiceUnit(du/3.0, &base, &power);
397   if (!isLog || reverted || unit>0.75) {
398     un= unit*floor(un/unit);
399     ux= unit*ceil(ux/unit);
400     if (reverted) {
401       un= log10(un);
402       ux= log10(ux);
403     }
404   } else {
405     /* subdecade log scale (sigh), use 2, 5, or 10 */
406     GpReal dn= floor(un+0.0001),   dx= ceil(ux-0.0001);
407     if (un<dn+(LOG2-0.0001)) un= dn;
408     else if (un<dn+(0.9999-LOG2)) un= dn+LOG2;
409     else un= dn+(1.0-LOG2);
410     if (ux>dx-(LOG2+0.0001)) ux= dx;
411     else if (ux>dx-(1.0001-LOG2)) ux= dx-LOG2;
412     else ux= ux-(1.0-LOG2);
413   }
414   *umin= un;
415   *umax= ux;
416 }
417 
EqAdjust(GpReal * umin,GpReal * umax)418 static void EqAdjust(GpReal *umin, GpReal *umax)
419 {
420   GpReal nudge= *umin>0.0? 0.001*(*umin) : -0.001*(*umin);
421   if (nudge==0.0) nudge= 1.0e-6;
422   *umin-= nudge;
423   *umax+= nudge;
424 }
425 
EmptyAdjust(GpReal * umin,GpReal * umax,int doMin,int doMax)426 static void EmptyAdjust(GpReal *umin, GpReal *umax, int doMin, int doMax)
427 {
428   if (doMin) {
429     if (doMax) { *umin= -1.0e-6; *umax= +1.0e-6; }
430     else if (*umax>0.0) *umin= 0.999*(*umax);
431     else if (*umax<0.0) *umin= 1.001*(*umax);
432     else *umin= -1.0e-6;
433   } else if (doMax) {
434     if (*umin>0.0) *umax= 1.001*(*umin);
435     else if (*umin<0.0) *umax= 0.999*(*umin);
436     else *umax= +1.0e-6;
437   } else if ((*umin)==(*umax)) {
438     EqAdjust(umin, umax);
439   }
440 }
441 
EqualAdjust(GpReal * umin,GpReal * umax,int doMin,int doMax)442 static void EqualAdjust(GpReal *umin, GpReal *umax, int doMin, int doMax)
443 {
444   if (doMin && doMax) EqAdjust(umin, umax);
445   else EmptyAdjust(umin, umax, doMin, doMax);
446 }
447 
GdScan(GeSystem * sys)448 int GdScan(GeSystem *sys)
449 {
450   int flags= sys->flags;
451   GpBox limits, tmp, *w= &sys->trans.window;
452   GpReal xmin= w->xmin, xmax= w->xmax, ymin= w->ymin, ymax= w->ymax;
453   int swapx, swapy;
454   GdElement *el, *el0= sys->elements;
455   int begin, damaged, first;
456 
457   /* Handle case of no curves (if, e.g., all elements removed) */
458   if (!el0) {
459     EmptyAdjust(&w->xmin, &w->xmax, flags&D_XMIN, flags&D_XMAX);
460     EmptyAdjust(&w->ymin, &w->ymax, flags&D_YMIN, flags&D_YMAX);
461     return 0;
462   }
463 
464   /* Assure that limits are ordered even if window is not */
465   swapx= (xmin > xmax) && !(flags&(D_XMIN|D_XMAX));
466   swapy= (ymin > ymax) && !(flags&(D_YMIN|D_YMAX));
467   if (!swapx) { limits.xmin= xmin;  limits.xmax= xmax; }
468   else { limits.xmin= xmax;  limits.xmax= xmin; }
469   if (!swapy) { limits.ymin= ymin;  limits.ymax= ymax; }
470   else { limits.ymin= ymax;  limits.ymax= ymin; }
471   tmp= limits;
472 
473   el= el0;
474   begin= sys->rescan? -1 : sys->unscanned;
475 
476   /* Scan limits for each element */
477   damaged= 0;
478   first= 1;
479   do {
480     if (!el->hidden) {
481       if (el->number>=begin) {
482         /* Scan ensures log values present, sets box, scans xy values */
483         if (el->ops->Scan(el, flags, &tmp)) return 1; /* mem failure */
484         if (first) {
485           /* first non-hidden element gives first cut at limits */
486           limits= tmp;
487           damaged= 1;
488         } else {
489           /* subsequent elements may cause limits to be adjusted */
490           if (tmp.xmin<=tmp.xmax) {
491             if (tmp.xmin<limits.xmin) limits.xmin= tmp.xmin;
492             if (tmp.xmax>limits.xmax) limits.xmax= tmp.xmax;
493           }
494           if (tmp.ymin<=tmp.ymax) {
495             if (tmp.ymin<limits.ymin) limits.ymin= tmp.ymin;
496             if (tmp.ymax>limits.ymax) limits.ymax= tmp.ymax;
497           }
498         }
499       }
500       first= 0;
501     }
502     el= el->next;
503   } while (el!=el0);
504 
505   /* #1- adjust if min==max */
506   if (limits.xmin==limits.xmax)
507     EqualAdjust(&limits.xmin, &limits.xmax, flags&D_XMIN, flags&D_XMAX);
508   if (limits.ymin==limits.ymax)
509     EqualAdjust(&limits.ymin, &limits.ymax, flags&D_XMIN, flags&D_XMAX);
510 
511   /* #2- adjust if log axis and minimum was SAFELOG(0) */
512   if ((flags & D_LOGX) && (flags & D_XMIN) && limits.xmin==SAFELOG0
513       && limits.xmax>SAFELOG0+10.0) limits.xmin= limits.xmax-10.0;
514   if ((flags & D_LOGY) && (flags & D_YMIN) && limits.ymin==SAFELOG0
515       && limits.ymax>SAFELOG0+10.0) limits.ymin= limits.ymax-10.0;
516 
517   /* #3- adjust if square limits specified and not semi-logarithmic */
518   if ((flags & D_SQUARE) &&
519       !(((flags&D_LOGX)!=0) ^ ((flags&D_LOGY)!=0))) {
520     /* (Square axes don't make sense for semi-log scales) */
521     GpReal dx= limits.xmax-limits.xmin;
522     GpReal dy= limits.ymax-limits.ymin;
523     GpReal dydx= (sys->trans.viewport.ymax-sys->trans.viewport.ymin)/
524                  (sys->trans.viewport.xmax-sys->trans.viewport.xmin);
525     /* Adjust y if either (1) dx>dy, or (2) x limits are both fixed
526        (NB- SquareAdjust is a noop if both limits fixed) */
527     if ((dx*dydx>dy && (flags&(D_YMIN|D_YMAX))) ||
528         !(flags&(D_XMIN|D_XMAX)))
529       SquareAdjust(&limits.ymin, &limits.ymax, dx*dydx,
530                    flags&D_YMIN, flags&D_YMAX);
531     else /* adjust x */
532       SquareAdjust(&limits.xmin, &limits.xmax, dy/dydx,
533                    flags&D_XMIN, flags&D_XMAX);
534   }
535 
536   /* #4- adjust if nice limits specified */
537   if (flags & D_NICE) {
538     NiceAdjust(&limits.xmin, &limits.xmax, flags&D_LOGX, flags&D_XMIN);
539     NiceAdjust(&limits.ymin, &limits.ymax, flags&D_LOGY, flags&D_YMIN);
540   }
541 
542   if (swapx) {
543     GpReal tmp= limits.xmin;  limits.xmin= limits.xmax;  limits.xmax= tmp;
544   }
545   if (swapy) {
546     GpReal tmp= limits.ymin;  limits.ymin= limits.ymax;  limits.ymax= tmp;
547   }
548   if (damaged || limits.xmin!=xmin || limits.xmax!=xmax ||
549       limits.ymin!=ymin || limits.ymax!=ymax)
550     Damage(sys, (GdElement *)0);
551   w->xmin= limits.xmin;
552   w->xmax= limits.xmax;
553   w->ymin= limits.ymin;
554   w->ymax= limits.ymax;
555 
556   sys->rescan= 0;
557   sys->unscanned= -1;
558 
559   return 0;
560 }
561 
Gd_DrawRing(void * elv,int xIsLog,int yIsLog,GeSystem * sys,int t)562 int Gd_DrawRing(void *elv, int xIsLog, int yIsLog, GeSystem *sys, int t)
563 {
564   GdElement *el0, *el= elv;
565   GpBox adjustBox, *box;
566   int value= 0, drawIt= t;
567   if ((el0= el)) {
568     do {
569       if (!t) {
570         if (!sys) {
571           el->ops->Margin(el, &adjustBox);
572           adjustBox.xmin+= el->box.xmin;
573           adjustBox.xmax+= el->box.xmax;
574           adjustBox.ymin+= el->box.ymin;
575           adjustBox.ymax+= el->box.ymax;
576           box= &adjustBox;
577         } else {
578           box= &sys->trans.viewport;
579         }
580         drawIt= GdBeginEl(box, el->number);
581       }
582       if (drawIt) value|= el->ops->Draw(el, xIsLog, yIsLog);
583       el= el->next;
584     } while (el!=el0);
585   }
586   return value;
587 }
588 
589 static GpTransform unitTrans= { {0., 2., 0., 2.}, {0., 2., 0., 2.} };
590 
GdDraw(int changesOnly)591 int GdDraw(int changesOnly)
592 {
593   int value= 0;
594   GpBox *damage;
595   int systemCounter;
596   int rescan= 0;
597 
598   if (!currentDr) return 1;
599 
600   if (changesOnly==-1) {
601     rescan= 1;
602     changesOnly= 0;
603   }
604 
605   /* Take care of conditional clear */
606   if (currentDr->cleared==1) {
607     if (changesOnly) return 0;
608     else ClearDrawing(currentDr);
609   }
610   if (!changesOnly || currentDr->cleared) {
611     GpClear(0, CONDITIONALLY);
612     currentDr->cleared= 0;
613   }
614 
615   /* Check if any coordinate systems need to be rescanned */
616   if (currentDr->systems) {
617     int changed;
618     GeSystem *sys, *sys0;
619     sys= sys0= currentDr->systems;
620     do {
621       if (rescan) sys->rescan= 1;
622       changed= (sys->rescan || sys->unscanned>=0);
623       if (changed) changesOnly= 0;
624       if (changed && GdScan(sys)) return 1;  /* memory manager failure */
625       sys= (GeSystem *)sys->el.next;
626     } while (sys!=sys0);
627   }
628 
629   /* Give engines a chance to prepare for a drawing */
630   if (currentDr->damaged) {
631     damage= &currentDr->damage;
632     currentDr->damaged= 0;
633   } else {
634     damage= 0;
635   }
636   /* GdBeginDr returns 1 if any active engine has been cleared or
637      partially cleared.  */
638   if (!GdBeginDr(currentDr, damage, currentDr->landscape) && changesOnly)
639     return 0;
640 
641   /* Do coordinate systems */
642   if (currentDr->systems) {
643     GeSystem *sys, *sys0;
644     sys= sys0= currentDr->systems;
645     systemCounter= 0;
646     do {
647       value|= SystemDraw(sys, systemCounter, 0);
648       systemCounter++;
649       sys= (GeSystem *)sys->el.next;
650     } while (sys!=sys0);
651   }
652 
653   /* Do elements outside of coordinate systems */
654   GpSetTrans(&unitTrans);
655   gistClip= 0;
656   value|= Gd_DrawRing(currentDr->elements, 0, 0, (GeSystem *)0, 0);
657 
658   /* Give engines a chance to clean up after a drawing */
659   GdEndDr();
660 
661   return value;
662 }
663 
664 /* ------------------------------------------------------------------------ */
665 /* Legend routines */
666 
GdLegendBox(int which,GpReal x,GpReal y,GpReal dx,GpReal dy,const GpTextAttribs * t,int nchars,int nlines,int nwrap)667 int GdLegendBox(int which, GpReal x, GpReal y, GpReal dx, GpReal dy,
668                 const GpTextAttribs *t, int nchars, int nlines, int nwrap)
669 {
670   GeLegendBox *lbox;
671   if (!currentDr || nchars<0) return 1;
672   lbox= currentDr->legends;
673   if (which) lbox++;
674   lbox->x= x;     lbox->y= y;
675   lbox->dx= dx;   lbox->dy= dy;
676   lbox->textStyle= *t;
677   lbox->nchars= nchars;
678   lbox->nlines= nlines;
679   lbox->nwrap= nwrap;
680   return 0;
681 }
682 
683 static char *legendText= 0;
684 static long lenLegends, maxLegends= 0;
685 
686 static int nRemaining, curWrap;
687 static char *curLegend;
688 static int curMarker= 0;
689 
690 static int doingContours, levelCurve, nLevels;
691 static GdElement *curElement, *cur0Element, *drElements, *curCon, *cur0Con;
692 static GeSystem *curSystem, *cur0System;
693 static GpReal *levelValue;
694 static GeLines **levelGroup;
695 static char levelLegend[32];
696 
InitLegends(int contours,GeSystem * systems,GdElement * elements,int size)697 static void InitLegends(int contours, GeSystem *systems, GdElement *elements,
698                         int size)
699 {
700   doingContours= levelCurve= contours;
701   if (doingContours) curCon= 0;
702   curElement= 0;
703   curSystem= cur0System= systems;
704   drElements= elements;
705   curLegend= 0;
706   curMarker= 0;
707   nRemaining= 0;
708 
709   if (size>maxLegends) {
710     if (legendText) p_free(legendText);
711     legendText= p_malloc((long)size);
712   }
713 }
714 
NextContours(void)715 static void NextContours(void)
716 {
717   if (!levelCurve) {
718     /* Set up for the ring of level curves */
719     GeContours *con= (GeContours *)curCon;
720     nLevels= con->nLevels;
721     levelValue= con->levels;
722     levelGroup= con->groups;
723     levelCurve= 1;
724     if (levelGroup) {
725       while (nLevels && !levelGroup[0]) {
726         levelValue++;
727         levelGroup++;
728         nLevels--;
729       }
730     } else {
731       nLevels= 0;
732     }
733     if (nLevels>0) curElement= (GdElement *)levelGroup[0];
734     else curElement= 0;
735     return;
736   }
737 
738   levelCurve= 0;
739   curElement= 0;
740   if (curCon) {
741     curCon= curCon->next;
742     if (curCon==cur0Con) curCon= 0;
743   }
744   for (;;) {
745     if (curCon) {
746       do {
747         if (curCon->ops->type==E_CONTOURS && !curCon->hidden) {
748           /* Set up for contour element itself-- terminates immediately */
749           curElement= curCon;
750           cur0Element= curElement->next;
751           return;
752         }
753         curCon= curCon->next;
754       } while (curCon!=cur0Con);
755     }
756 
757     if (curSystem) {
758       curCon= cur0Con= curSystem->elements;
759       curSystem= (GeSystem *)curSystem->el.next;
760       if (curSystem==cur0System) curSystem= 0;
761     } else if (drElements) {
762       curCon= cur0Con= drElements;
763       drElements= 0;
764     } else {
765       break;
766     }
767   }
768 }
769 
NextRing(void)770 static int NextRing(void)
771 {
772   if (doingContours) {
773     NextContours();
774     if (!curElement) return 0;
775   } else if (curSystem) {
776     curElement= cur0Element= curSystem->elements;
777     curSystem= (GeSystem *)curSystem->el.next;
778     if (curSystem==cur0System) curSystem= 0;
779   } else if (drElements) {
780     curElement= cur0Element= drElements;
781     drElements= 0;
782   } else {
783     return 0;
784   }
785   return 1;
786 }
787 
788 static int specialMarks[5]= { '.', '+', '*', 'o', 'x' };
789 
NextLegend(void)790 static int NextLegend(void)
791 {
792   curLegend= 0;
793   curMarker= 0;
794   do {
795     while (curElement) {
796       if (!curElement->hidden) {
797         int type= curElement->ops->type;
798         if (curElement->legend) curLegend= curElement->legend;
799         else if (levelCurve) {
800           /* automatically generate level curve legend if not supplied */
801           curLegend= levelLegend;
802           sprintf(curLegend, "\001: %.4g", *levelValue);
803         }
804         if (curLegend) {
805           nRemaining= strlen(curLegend);
806           curWrap= 0;
807           if ((type==E_LINES || type==E_CONTOURS) && curLegend[0]=='\001') {
808             /* insert marker into E_LINES legend if so directed */
809             curMarker= type==E_LINES? ((GeLines *)curElement)->m.type :
810             ((GeContours *)curElement)->m.type;
811             if (curMarker>=1 && curMarker<=5)
812               curMarker= specialMarks[curMarker-1];
813             else if (curMarker<' ' || curMarker>='\177')
814               curMarker= ' ';
815           }
816         }
817       }
818       if (levelCurve) {
819         do {
820           levelValue++;
821           levelGroup++;
822           nLevels--;
823         } while (nLevels && !levelGroup[0]);
824         if (nLevels>0) curElement= (GdElement *)levelGroup[0];
825         else curElement= 0;
826       } else {
827         curElement= curElement->next;
828         if (curElement==cur0Element) curElement= 0;
829       }
830       if (curLegend) return 1;
831     }
832   } while (NextRing());
833   return 0;
834 }
835 
BuildLegends(int more,int contours,GeSystem * systems,GdElement * elements,GeLegendBox * lbox)836 static int BuildLegends(int more, int contours, GeSystem *systems,
837                         GdElement *elements, GeLegendBox *lbox)
838 {
839   int firstLine= 1;
840   int nlines= lbox->nlines;
841   int nchars= lbox->nchars;
842   int nwrap= lbox->nwrap;
843   int nc;
844 
845   lenLegends= 0;
846   if (!more) {
847     if (nlines<=0 || nchars<=0) return 0;
848     InitLegends(contours, systems, elements, (nchars+1)*nlines);
849     if (!legendText) return 0;
850   }
851 
852   for ( ; ; nlines--) {
853     if (!curLegend && !NextLegend()) { more= 0;   break; }
854     if (nlines<=0) { more= !more;   break; }
855     if (firstLine) firstLine= 0;
856     else legendText[lenLegends++]= '\n';
857     nc= nRemaining>nchars? nchars : nRemaining;
858     strncpy(legendText+lenLegends, curLegend, nc);
859     if (curMarker) {
860       legendText[lenLegends]= (char)curMarker;
861       curMarker= 0;
862     }
863     lenLegends+= nc;
864     nRemaining-= nc;
865     if (nRemaining>0 && curWrap++<nwrap) curLegend+= nc;
866     else { curLegend= 0; curMarker= 0; }
867   }
868 
869   legendText[lenLegends]= '\0';
870   return more;
871 }
872 
GdDrawLegends(Engine * engine)873 int GdDrawLegends(Engine *engine)
874 {
875   GpReal x, y;
876   int type, more;
877   GeLegendBox *lbox;
878   if (!currentDr) return 1;
879 
880   if (engine) GpPreempt(engine);
881 
882   for (type=0 ; type<2 ; type++) {
883     lbox= &currentDr->legends[type];
884     x= lbox->x;
885     y= lbox->y;
886     gistA.t= lbox->textStyle;
887     GpSetTrans(&unitTrans);
888     gistClip= 0;
889     if (lbox->nlines <= 0) continue;
890     for (more=0 ; ; ) {
891       more= BuildLegends(more, type, currentDr->systems, currentDr->elements,
892                          lbox);
893       if (!legendText) {
894         /* memory error */
895         if (engine) GpPreempt(0);
896         return 1;
897       }
898       if (lenLegends>0) GpText(x, y, legendText);
899       if (!more || (lbox->dx==0.0 && lbox->dy==0.0)) break;
900       x+= lbox->dx;
901       y+= lbox->dy;
902     }
903   }
904 
905   if (engine) GpPreempt(0);
906   return 0;
907 }
908 
909 /* ------------------------------------------------------------------------ */
910 /* Utility routines */
911 
MemoryError(void)912 static int MemoryError(void)
913 {
914   if (currentDr)
915     strcpy(gistError, "memory manager failed in Gd function");
916   else
917     strcpy(gistError, "currentDr not set in Gd function");
918   return -1;
919 }
920 
Copy1(const void * orig,long size)921 static void *Copy1(const void *orig, long size)
922 {
923   void *px;
924   if (size<=0) return 0;
925   px= p_malloc(size);
926   if (!px) MemoryError();
927   else if (orig) memcpy(px, orig, size);
928   return px;
929 }
930 
Copy2(void * x1,const void * orig1,const void * orig2,long size)931 static void *Copy2(void *x1, const void *orig1, const void *orig2, long size)
932 {
933   void *x2, **x1p= (void **)x1;
934   *x1p= Copy1(orig1, size);
935   if (!*x1p) return 0;
936   x2= Copy1(orig2, size);
937   if (!x2) { p_free(*x1p);  *x1p= 0; }
938   return x2;
939 }
940 
Gd_ScanZ(long n,const GpReal * z,GpReal * zmin,GpReal * zmax)941 void Gd_ScanZ(long n, const GpReal *z, GpReal *zmin, GpReal *zmax)
942 {
943   long i;
944   GpReal zn, zx;
945   zn= zx= z[0];
946   for (i=1 ; i<n ; i++) {
947     if (z[i]<zn) zn= z[i];
948     else if (z[i]>zx) zx= z[i];
949   }
950   *zmin= zn;
951   *zmax= zx;
952 }
953 
ScanXY(long n,const GpReal * x,const GpReal * y,GpBox * extrema)954 static void ScanXY(long n, const GpReal *x, const GpReal *y, GpBox *extrema)
955 {
956   Gd_ScanZ(n, x, &extrema->xmin, &extrema->xmax);
957   Gd_ScanZ(n, y, &extrema->ymin, &extrema->ymax);
958 }
959 
GeAddElement(int type,GdElement * element)960 void GeAddElement(int type, GdElement *element)
961 {
962   GdElement *old;
963   Drauing *drawing= currentDr;
964   GeSystem *sys;
965 
966   if (drawing->cleared==1) ClearDrawing(drawing);
967   sys= currentSy;
968 
969   old= sys? sys->elements : drawing->elements;
970   if (!old) {  /* this is first element */
971     if (sys) sys->elements= element;
972     else drawing->elements= element;
973     element->prev= element->next= element;
974   } else {     /* insert element at end of ring */
975     element->prev= old->prev;
976     element->next= old;
977     old->prev= element->prev->next= element;
978   }
979   element->ops= opTables + type;
980   element->hidden= gistD.hidden;
981   if (gistD.legend) {
982     element->legend= Copy1(gistD.legend, strlen(gistD.legend)+1);
983     /* sigh. ignore memory error here */
984   } else {
985     element->legend= 0;
986   }
987   element->number= drawing->nElements++;
988   /* System nust always have number of its largest element for
989      GdBeginSy to work properly */
990   if (sys) sys->el.number= element->number;
991   else Damage((GeSystem *)0, element);
992 }
993 
Gd_NextMeshBlock(long * ii,long * jj,long len,long iMax,int * reg,int region)994 void Gd_NextMeshBlock(long *ii, long *jj, long len, long iMax,
995                       int *reg, int region)
996 {   /* Find next contiguous run of mesh points in given region */
997   long i= *ii;
998   long j= *jj;
999   if (region==0) {
1000     for (j=i ; j<len ; j++)
1001       if (reg[j] || reg[j+1] || reg[j+iMax] || reg[j+iMax+1]) break;
1002     i= j;
1003     for (j=i+1 ; j<len ; j++)
1004       if (!reg[j] && !reg[j+1] && !reg[j+iMax] && !reg[j+iMax+1]) break;
1005   } else {
1006     for (j=i ; j<len ; j++)
1007       if (reg[j]==region || reg[j+1]==region ||
1008           reg[j+iMax]==region || reg[j+iMax+1]==region) break;
1009     i= j;
1010     for (j=i+1 ; j<len ; j++)
1011       if (reg[j]!=region && reg[j+1]!=region &&
1012           reg[j+iMax]!=region && reg[j+iMax+1]!=region) break;
1013   }
1014   *ii= i;
1015   *jj= j;
1016 }
1017 
GeGetMesh(int noCopy,GaQuadMesh * meshin,int region,void * vMeshEl)1018 long GeGetMesh(int noCopy, GaQuadMesh *meshin, int region, void *vMeshEl)
1019 {
1020   GeMesh *meshEl= vMeshEl;
1021   GaQuadMesh *mesh= &meshEl->mesh;
1022   GpBox *linBox= &meshEl->linBox;
1023   long iMax, jMax, i, j, len;
1024   int *reg;
1025 
1026   if (currentDr->cleared==1) ClearDrawing(currentDr);
1027 
1028   /* retrieve mesh shape from meshin */
1029   mesh->iMax= iMax= meshin->iMax;
1030   mesh->jMax= jMax= meshin->jMax;
1031   len= iMax*jMax;
1032 
1033   mesh->reg= 0;       /* set up for possible error return */
1034   mesh->triangle= 0;  /* only needed by GdContours, so handled there */
1035 
1036   meshEl->xlog= meshEl->ylog= 0;
1037   meshEl->region= region;
1038 
1039   meshEl->noCopy= noCopy & (NOCOPY_MESH|NOCOPY_COLORS|NOCOPY_UV|NOCOPY_Z);
1040   if (noCopy&NOCOPY_MESH) {
1041     /* Just copy pointers to mesh arrays -- NOCOPY also means not
1042        to free the pointer later */
1043     mesh->x= meshin->x;
1044     mesh->y= meshin->y;
1045   } else {
1046     /* Copy the mesh arrays themselves */
1047     mesh->y= Copy2(&mesh->x, meshin->x, meshin->y, sizeof(GpReal)*len);
1048     if (!mesh->y) { p_free(vMeshEl);  return 0; }
1049   }
1050 
1051   if ((noCopy&NOCOPY_MESH) && meshin->reg) {
1052     mesh->reg= reg= meshin->reg;
1053     meshEl->noCopy|= NOCOPY_REG;
1054   } else {
1055     mesh->reg= reg= Copy1(meshin->reg, sizeof(int)*(len+iMax+1));
1056     if (!reg) { Gd_KillMeshXY(vMeshEl);  p_free(vMeshEl);  return 0; }
1057   }
1058 
1059   /* Be sure region array is legal */
1060   for (i=0 ; i<iMax ; i++) reg[i]= 0;
1061   if (!meshin->reg) for (i=iMax ; i<len ; i++) reg[i]= 1;
1062   for (i=len ; i<len+iMax+1 ; i++) reg[i]= 0;
1063   for (i=0 ; i<len ; i+=iMax) reg[i]= 0;
1064 
1065   /* Scan mesh for extreme values */
1066   if (meshin->reg) {
1067     GpBox box;
1068     int first= 1;
1069 
1070     /* Use ScanXY on the longest contiguous run(s) of points */
1071     for (i=0 ; i<len ; ) {
1072       Gd_NextMeshBlock(&i, &j, len, iMax, reg, region);
1073       if (i>=len) break;
1074       ScanXY(j-i, mesh->x+i, mesh->y+i, &box);
1075       if (first) { *linBox= box;  first= 0; }
1076       else GpSwallow(linBox, &box);
1077       i= j+1;
1078     }
1079     if (first)
1080       linBox->xmin= linBox->xmax= linBox->ymin= linBox->ymax= 0.0;
1081 
1082   } else {
1083     ScanXY(len, mesh->x, mesh->y, linBox);
1084   }
1085   if (!currentSy) meshEl->el.box= *linBox;  /* for GeAddElement */
1086 
1087   /* copy mesh properties to gistD */
1088   Gd_MeshXYGet(vMeshEl);
1089 
1090   return len;
1091 }
1092 
GeMarkForScan(GdElement * el,GpBox * linBox)1093 void GeMarkForScan(GdElement *el, GpBox *linBox)
1094 {
1095   if (currentSy) {
1096     if (currentSy->unscanned<0) currentSy->unscanned= el->number;
1097   } else {
1098     el->box= *linBox;
1099   }
1100 }
1101 
AutoMarker(GaLineAttribs * dl,int number)1102 static int AutoMarker(GaLineAttribs *dl, int number)
1103 {
1104   int p;
1105   p= (number+3)%4;
1106   if (number>=26) number%= 26;
1107   dl->mPhase= 0.25*(0.5+p)*dl->mSpace;
1108   dl->rPhase= 0.25*(0.5+p)*dl->rSpace;
1109   return 'A'+number;
1110 }
1111 
1112 /* ------------------------------------------------------------------------ */
1113 /* Constructors for drawing elements are public routines declared in gist.h */
1114 
GdLines(long n,const GpReal * px,const GpReal * py)1115 int GdLines(long n, const GpReal *px, const GpReal *py)
1116 {
1117   GeLines *el;
1118   if (n<=0) return -1;
1119   el= currentDr? p_malloc(sizeof(GeLines)) : 0;
1120   if (!el) return MemoryError();
1121   el->xlog= el->ylog= 0;
1122 
1123   /* make private copies of x and y arrays */
1124   el->y= Copy2(&el->x, px, py, sizeof(GpReal)*n);
1125   if (!el->y) { p_free(el);  return -1; }
1126   el->n= n;
1127 
1128   /* scan for min and max of x and y arrays */
1129   ScanXY(n, px, py, &el->linBox);
1130   if (!currentSy) el->el.box= el->linBox;  /* for GeAddElement */
1131 
1132   /* copy relevant attributes from gistA
1133      -- This must be done BEFORE GeAddElement, since the damage
1134         calculation depends on the Margins! */
1135   el->l= gistA.l;
1136   el->dl= gistA.dl;
1137   el->m= gistA.m;
1138 
1139   /* set base class members */
1140   GeAddElement(E_LINES, &el->el);
1141   if (gistA.m.type==0) el->m.type= AutoMarker(&el->dl, el->el.number);
1142 
1143   /* current box not set, mark as unscanned if in system */
1144   GeMarkForScan(&el->el, &el->linBox);
1145 
1146   /* copy properties to gistD */
1147   gistD.n= n;
1148   gistD.x= el->x;
1149   gistD.y= el->y;
1150 
1151   return el->el.number;
1152 }
1153 
GdDisjoint(long n,const GpReal * px,const GpReal * py,const GpReal * qx,const GpReal * qy)1154 int GdDisjoint(long n, const GpReal *px, const GpReal *py,
1155                const GpReal *qx, const GpReal *qy)
1156 {
1157   GeDisjoint *el;
1158   GpBox box;
1159   if (n<=0) return -1;
1160   el= currentDr? p_malloc(sizeof(GeDisjoint)) : 0;
1161   if (!el) return MemoryError();
1162   el->el.next= el->el.prev= 0;
1163   el->xlog= el->ylog= el->xqlog= el->yqlog= 0;
1164 
1165   /* make private copies of x, y, xq, yq arrays */
1166   el->y= Copy2(&el->x, px, py, sizeof(GpReal)*n);
1167   if (!el->y) { p_free(el);  return -1; }
1168   el->yq= Copy2(&el->xq, qx, qy, sizeof(GpReal)*n);
1169   if (!el->yq) { DisjointKill(el);  return -1; }
1170   el->n= n;
1171 
1172   /* scan for min and max of x and y arrays */
1173   ScanXY(n, px, py, &box);
1174   ScanXY(n, qx, qy, &el->linBox);
1175   GpSwallow(&el->linBox, &box);
1176   if (!currentSy) el->el.box= el->linBox;  /* for GeAddElement */
1177 
1178   /* copy relevant attributes from gistA
1179      -- This must be done BEFORE GeAddElement, since the damage
1180         calculation depends on the Margins! */
1181   el->l= gistA.l;
1182 
1183   /* set base class members */
1184   GeAddElement(E_DISJOINT, &el->el);
1185 
1186   /* current box not set, mark as unscanned if in system */
1187   GeMarkForScan(&el->el, &el->linBox);
1188 
1189   /* copy properties to gistD */
1190   gistD.n= n;
1191   gistD.x= el->x;
1192   gistD.y= el->y;
1193   gistD.xq= el->xq;
1194   gistD.yq= el->yq;
1195 
1196   return el->el.number;
1197 }
1198 
GdText(GpReal x0,GpReal y0,const char * text,int toSys)1199 int GdText(GpReal x0, GpReal y0, const char *text, int toSys)
1200 {
1201   GeText *el= currentDr? p_malloc(sizeof(GeText)) : 0;
1202   GeSystem *sys= currentSy;
1203   if (!el) return MemoryError();
1204 
1205   /* make private copy of text string */
1206   el->text= Copy1(text, strlen(text)+1);
1207   if (!el->text) { p_free(el);  return -1; }
1208   el->x0= x0;
1209   el->y0= y0;
1210 
1211   /* Without some sort of common font metric, there is no way to
1212      know the box associated with text.  Even with such a metric,
1213      the box would have to change with the coordinate transform,
1214      unlike any other element.  For now, punt.  */
1215   el->el.box.xmin= el->el.box.xmax= x0;
1216   el->el.box.ymin= el->el.box.ymax= y0;
1217 
1218   /* copy relevant attributes from gistA
1219      -- This must be done BEFORE GeAddElement, since the damage
1220         calculation depends on the Margins! */
1221   el->t= gistA.t;
1222 
1223   /* set base class members */
1224   if (currentDr->cleared==1) ClearDrawing(currentDr); /* else currentSy */
1225   if (!toSys) currentSy= 0;                      /* can be clobbered... */
1226   GeAddElement(E_TEXT, &el->el);
1227   if (currentSy && currentSy->unscanned<0)
1228     currentSy->unscanned= el->el.number;
1229   if (!toSys) currentSy= sys;
1230 
1231   /* copy properties to gistD */
1232   gistD.x0= el->x0;
1233   gistD.y0= el->y0;
1234   gistD.text= el->text;
1235 
1236   return el->el.number;
1237 }
1238 
GdCells(GpReal px,GpReal py,GpReal qx,GpReal qy,long width,long height,long nColumns,const GpColor * colors)1239 int GdCells(GpReal px, GpReal py, GpReal qx, GpReal qy,
1240             long width, long height, long nColumns, const GpColor *colors)
1241 {
1242   GpReal x[2], y[2];
1243   GpBox linBox;
1244   long ncells= width*height;
1245   long len= sizeof(GpColor)*ncells;
1246   GeCells *el= currentDr? p_malloc(sizeof(GeCells)) : 0;
1247   if (!el) return MemoryError();
1248 
1249   /* make private copy of colors array */
1250   el->rgb = gistA.rgb;
1251   if (gistA.rgb) len *= 3;
1252   gistA.rgb = 0;
1253   el->colors= p_malloc(len);
1254   if (!el->colors) { p_free(el); return MemoryError(); }
1255   el->px= x[0]= px;
1256   el->py= y[0]= py;
1257   el->qx= x[1]= qx;
1258   el->qy= y[1]= qy;
1259   el->width= width;
1260   el->height= height;
1261   if (nColumns==width) {
1262     memcpy(el->colors, colors, len);
1263   } else {
1264     GpColor *newcols= el->colors;
1265     long i, rowSize= sizeof(GpColor)*width;
1266     for (i=0 ; i<height ; i++) {
1267       memcpy(newcols, colors, rowSize);
1268       newcols+= width;
1269       colors+= nColumns;
1270     }
1271   }
1272   ScanXY(2L, x, y, &linBox);
1273   if (!currentSy) el->el.box= linBox;  /* for GeAddElement */
1274 
1275   /* set base class members */
1276   GeAddElement(E_CELLS, &el->el);
1277 
1278   /* current box not set, mark as unscanned if in system */
1279   GeMarkForScan(&el->el, &linBox);
1280 
1281   /* copy properties to gistD */
1282   gistD.px= el->px;
1283   gistD.py= el->py;
1284   gistD.qx= el->qx;
1285   gistD.qy= el->qy;
1286   gistD.width= el->width;
1287   gistD.height= el->height;
1288   gistD.colors= el->colors;
1289 
1290   return el->el.number;
1291 }
1292 
GdFill(long n,const GpColor * colors,const GpReal * px,const GpReal * py,const long * pn)1293 int GdFill(long n, const GpColor *colors, const GpReal *px,
1294            const GpReal *py, const long *pn)
1295 {
1296   GePolys *el;
1297   long i, ntot;
1298   if (n<=0) return -1;
1299   el= currentDr? p_malloc(sizeof(GePolys)) : 0;
1300   if (!el) return MemoryError();
1301   el->xlog= el->ylog= 0;
1302 
1303   /* make private copy of colors array */
1304   if (colors) {
1305     long ncol = (gistA.rgb? 3*n : n);
1306     el->rgb = gistA.rgb;
1307     el->colors= p_malloc(ncol);
1308     if (!el->colors) { p_free(el); return MemoryError(); }
1309     memcpy(el->colors, colors, ncol);
1310   } else {
1311     el->rgb = 0;
1312     el->colors= 0;
1313   }
1314   gistA.rgb = 0;
1315 
1316   /* make private copy of lengths array */
1317   el->pn= p_malloc(sizeof(long)*n);
1318   if (!el->pn) { p_free(el->colors); p_free(el); return MemoryError(); }
1319   for (ntot=i=0 ; i<n ; i++) {
1320     el->pn[i]= pn[i];
1321     ntot+= pn[i];
1322   }
1323 
1324   /* make private copies of x and y arrays */
1325   el->y= Copy2(&el->x, px, py, sizeof(GpReal)*ntot);
1326   if (!el->y) { p_free(el->pn); p_free(el->colors); p_free(el);  return -1; }
1327   el->n= n;
1328 
1329   /* scan for min and max of x and y arrays */
1330   if (n<2 || pn[1]>1) ScanXY(ntot, px, py, &el->linBox);
1331   else ScanXY(ntot-pn[0], px+pn[0], py+pn[0], &el->linBox);
1332   if (!currentSy) el->el.box= el->linBox;  /* for GeAddElement */
1333 
1334   /* copy relevant attributes from gistA
1335      -- This must be done BEFORE GeAddElement, since the damage
1336         calculation depends on the Margins! */
1337   el->e= gistA.e;  /* for edges */
1338 
1339   /* set base class members */
1340   GeAddElement(E_POLYS, &el->el);
1341 
1342   /* current box not set, mark as unscanned if in system */
1343   GeMarkForScan(&el->el, &el->linBox);
1344 
1345   /* copy properties to gistD */
1346   gistD.n= n;
1347   gistD.x= el->x;
1348   gistD.y= el->y;
1349   gistD.pn= el->pn;
1350   gistD.colors= el->colors;
1351 
1352   return el->el.number;
1353 }
1354 
GdMesh(int noCopy,GaQuadMesh * mesh,int region,int boundary,int inhibit)1355 int GdMesh(int noCopy, GaQuadMesh *mesh, int region, int boundary,
1356            int inhibit)
1357 {
1358   long len;
1359   GeMesh *el= currentDr? p_malloc(sizeof(GeMesh)) : 0;
1360   if (!el) return MemoryError();
1361   el->el.next= el->el.prev= 0;
1362 
1363   /* get mesh */
1364   len= GeGetMesh(noCopy, mesh, region, el);
1365   if (!len) return -1;
1366   el->boundary= boundary;
1367   el->inhibit= inhibit;
1368 
1369   /* copy relevant attributes from gistA
1370      -- This must be done BEFORE GeAddElement, since the damage
1371         calculation depends on the Margins! */
1372   el->l= gistA.l;
1373 
1374   /* set base class members */
1375   GeAddElement(E_MESH, &el->el);
1376 
1377   /* current box not set, mark as unscanned if in system */
1378   GeMarkForScan(&el->el, &el->linBox);
1379 
1380   /* copy properties to gistD */
1381   gistD.boundary= el->boundary;
1382   gistD.inhibit= el->inhibit;
1383 
1384   return el->el.number;
1385 }
1386 
GdFillMesh(int noCopy,GaQuadMesh * mesh,int region,GpColor * colors,long nColumns)1387 int GdFillMesh(int noCopy, GaQuadMesh *mesh, int region,
1388                GpColor *colors, long nColumns)
1389 {
1390   long len;
1391   GeFill *el= currentDr? p_malloc(sizeof(GeFill)) : 0;
1392   if (!el) return MemoryError();
1393   el->el.next= el->el.prev= 0;
1394 
1395   /* get mesh */
1396   len= GeGetMesh(noCopy, mesh, region, el);
1397   if (!len) return -1;
1398 
1399   /* make private copy of colors array */
1400   el->rgb = gistA.rgb;
1401   if (noCopy&NOCOPY_COLORS || !colors) {
1402     el->colors= colors;
1403   } else {
1404     long iMax1= mesh->iMax-1;
1405     long len1= len - mesh->jMax - iMax1;
1406     int rgb = gistA.rgb;
1407     if (rgb) len1 *= 3;
1408     el->colors= Copy1(nColumns==iMax1?colors:0, sizeof(GpColor)*len1);
1409     if (!el->colors) { FilledKill(el);  return -1; }
1410     if (nColumns!=iMax1) {
1411       long i, j=0, k=0;
1412       for (i=0 ; i<len1 ; i++) {
1413         if (rgb) {
1414           el->colors[i++]= colors[3*(j+k)];
1415           el->colors[i++]= colors[3*(j+k)+1];
1416           el->colors[i]= colors[3*(j+k)+2];
1417         } else {
1418           el->colors[i]= colors[j+k];
1419         }
1420         j++;
1421         if (j==iMax1) { k+= nColumns; j= 0; }
1422       }
1423       nColumns= iMax1;
1424     }
1425   }
1426   gistA.rgb = 0;
1427   el->nColumns= nColumns;
1428 
1429   /* copy relevant attributes from gistA
1430      -- This must be done BEFORE GeAddElement, since the damage
1431         calculation depends on the Margins! */
1432   el->e= gistA.e;  /* for edges */
1433 
1434   /* set base class members */
1435   GeAddElement(E_FILLED, &el->el);
1436 
1437   /* current box not set, mark as unscanned if in system */
1438   GeMarkForScan(&el->el, &el->linBox);
1439 
1440   /* copy properties to gistD */
1441   gistD.nColumns= nColumns;
1442   gistD.colors= el->colors;
1443 
1444   return el->el.number;
1445 }
1446 
GdVectors(int noCopy,GaQuadMesh * mesh,int region,GpReal * u,GpReal * v,GpReal scale)1447 int GdVectors(int noCopy, GaQuadMesh *mesh, int region,
1448               GpReal *u, GpReal *v, GpReal scale)
1449 {
1450   long len;
1451   GeVectors *el= currentDr? p_malloc(sizeof(GeVectors)) : 0;
1452   if (!el) return MemoryError();
1453   el->el.next= el->el.prev= 0;
1454 
1455   /* get mesh */
1456   len= GeGetMesh(noCopy, mesh, region, el);
1457   if (!len) return -1;
1458 
1459   /* make private copy of (u,v) arrays */
1460   if (noCopy&NOCOPY_UV) {
1461     el->u= u;
1462     el->v= v;
1463   } else {
1464     el->v= Copy2(&el->u, u, v, sizeof(GpReal)*len);
1465     if (!el->v) { VectorsKill(el);  return -1; }
1466   }
1467   el->scale= scale;
1468 
1469   /* copy relevant attributes from gistA
1470      -- This must be done BEFORE GeAddElement, since the damage
1471         calculation depends on the Margins! */
1472   el->l= gistA.l;
1473   el->f= gistA.f;
1474   el->vect= gistA.vect;
1475 
1476   /* set base class members */
1477   GeAddElement(E_VECTORS, &el->el);
1478 
1479   /* current box not set, mark as unscanned if in system */
1480   GeMarkForScan(&el->el, &el->linBox);
1481 
1482   /* copy properties to gistD */
1483   gistD.u= el->u;
1484   gistD.v= el->v;
1485   gistD.scale= el->scale;
1486 
1487   return el->el.number;
1488 }
1489 
Gd_MakeContours(GeContours * con)1490 int Gd_MakeContours(GeContours *con)
1491 {
1492   long n;
1493   GpReal dphase, *px, *py;
1494   int i;
1495   GeLines *group;
1496   GeLines *el, *prev;
1497   int marker;
1498 
1499   /* Generic properties copied to all contours (m.type reset below)  */
1500   gistA.l= con->l;
1501   gistA.dl= con->dl;
1502   gistA.m= con->m;
1503   marker= gistA.m.type>32? gistA.m.type : 'A';
1504   dphase= 0.25*con->dl.mSpace;
1505 
1506   for (i=0 ; i<con->nLevels ; i++) con->groups[i]= 0;
1507 
1508   for (i=0 ; i<con->nLevels ; i++) {
1509     gistA.m.type= marker++;
1510     if (marker=='Z'+1 || marker=='z'+1) marker= 'A';
1511     group= prev= 0;
1512     if (GaContourInit(&con->mesh, con->region, con->z, con->levels[i])) {
1513       while (GaContour(&n, &px, &py, &gistA.dl.closed)) {
1514         el= currentDr? p_malloc(sizeof(GeLines)) : 0;
1515         if (!el) return MemoryError();
1516 
1517         /* make private copies of x and y arrays */
1518         el->y= Copy2(&el->x, px, py, sizeof(GpReal)*n);
1519         if (!el->y) { p_free(el);  return -1; }
1520         el->n= n;
1521         el->xlog= el->ylog= 0;
1522 
1523         /* scan for min and max of x and y arrays */
1524         ScanXY(n, px, py, &el->linBox);
1525         if (!currentSy) el->el.box= el->linBox;  /* for GeAddElement */
1526 
1527         el->el.ops= opTables + E_LINES;
1528         el->el.hidden= 0;
1529         el->el.legend= 0;
1530         el->el.box= el->linBox;
1531 
1532         /* GeContour number always matches number of largest element
1533            for GdBeginSy to work properly */
1534         el->el.number= con->el.number= currentDr->nElements++;
1535 
1536         if (prev) {
1537           prev->el.next= group->el.prev= &el->el;
1538           el->el.prev= &prev->el;
1539           el->el.next= &group->el;
1540         } else {
1541           con->groups[i]= group= el;
1542           el->el.next= el->el.prev= &el->el;
1543         }
1544         prev= el;
1545 
1546         el->l= gistA.l;
1547         el->dl= gistA.dl;
1548         el->m= gistA.m;
1549 
1550         gistA.dl.mPhase+= dphase;
1551         if (gistA.dl.mPhase>gistA.dl.mSpace)
1552           gistA.dl.mPhase-= gistA.dl.mSpace;
1553       }
1554     }
1555   }
1556 
1557   return 0;
1558 }
1559 
GdContours(int noCopy,GaQuadMesh * mesh,int region,GpReal * z,const GpReal * levels,int nLevels)1560 int GdContours(int noCopy, GaQuadMesh *mesh, int region,
1561                GpReal *z, const GpReal *levels, int nLevels)
1562 {
1563   long len;
1564   GeContours *el= currentDr? p_malloc(sizeof(GeContours)) : 0;
1565   if (!el) return MemoryError();
1566   el->el.next= el->el.prev= 0;
1567   el->z= el->levels= 0;
1568   el->groups= 0;
1569 
1570   /* get mesh */
1571   len= GeGetMesh(noCopy, mesh, region, el);
1572   if (!len) return -1;
1573 
1574   /* make private copy of z and levels arrays */
1575   if (noCopy&NOCOPY_Z) {
1576     el->z= z;
1577   } else {
1578     el->z= Copy1(z, sizeof(GpReal)*len);
1579     if (!el->z) { ContoursKill(el);  return -1; }
1580   }
1581 
1582   /* create triangle array now if necessary */
1583   if (noCopy&NOCOPY_MESH && mesh->triangle) {
1584     el->mesh.triangle= mesh->triangle;
1585     el->noCopy|= NOCOPY_TRI;
1586     gistD.noCopy|= NOCOPY_TRI;
1587   } else {
1588     el->mesh.triangle= Copy1(mesh->triangle, sizeof(short)*len);
1589     if (!el->mesh.triangle) { ContoursKill(el);  return -1; }
1590   }
1591 
1592   /* copy relevant attributes from gistA
1593      -- This must be done BEFORE GeAddElement, since the damage
1594         calculation depends on the Margins! */
1595   el->l= gistA.l;
1596   el->dl= gistA.dl;
1597   el->m= gistA.m;
1598 
1599   /* set base class members */
1600   GeAddElement(E_CONTOURS, &el->el);
1601   if (gistA.m.type==0) el->m.type= AutoMarker(&el->dl, el->el.number);
1602 
1603   el->nLevels= nLevels;
1604   if (nLevels>0) {
1605     el->levels= Copy1(levels, sizeof(GpReal)*nLevels);
1606     if (!el->levels) { ContoursKill(el);  return -1; }
1607     el->groups= (GeLines **)p_malloc(sizeof(GeLines *)*nLevels);
1608     if (!el->groups || Gd_MakeContours(el)) { ContoursKill(el);  return -1; }
1609   } else {
1610     nLevels= 0;
1611     el->levels= 0;
1612     el->groups= 0;
1613   }
1614 
1615   /* current box not set, mark as unscanned if in system */
1616   GeMarkForScan(&el->el, &el->linBox);
1617 
1618   /* copy properties to gistD */
1619   gistD.z= el->z;
1620   gistD.nLevels= el->nLevels;
1621   gistD.levels= el->levels;
1622 
1623   return el->el.number;
1624 }
1625 
1626 /* ------------------------------------------------------------------------ */
1627 
GuessBox(GpBox * box,GpBox * viewport,GaTickStyle * ticks)1628 static void GuessBox(GpBox *box, GpBox *viewport, GaTickStyle *ticks)
1629 {
1630   GpReal dxmin= 0.0, xmin= viewport->xmin;
1631   GpReal dxmax= 0.0, xmax= viewport->xmax;
1632   GpReal dymin= 0.0, ymin= viewport->ymin;
1633   GpReal dymax= 0.0, ymax= viewport->ymax;
1634   int vf= ticks->vert.flags;
1635   int hf= ticks->horiz.flags;
1636   GpReal vlen= ((((vf&TICK_IN)&&(vf&TICK_OUT))||(vf&TICK_C))? 0.5 : 1.0) *
1637     ticks->vert.tickLen[0];
1638   GpReal hlen= ((((vf&TICK_IN)&&(vf&TICK_OUT))||(vf&TICK_C))? 0.5 : 1.0) *
1639     ticks->horiz.tickLen[0];
1640   GpReal cy= ticks->horiz.textStyle.height;
1641   GpReal cx= ticks->vert.textStyle.height*0.6;  /* guess at char width */
1642   GpReal hx= cy*0.6;                            /* guess at char width */
1643   GpBox overflow;
1644 
1645   /* Note-- extra 0.4 for nudged log decades (see DrawXLabels, tick.c) */
1646   cx*= ticks->vert.nDigits+2.4;         /* largest width of y label */
1647   hx*= 0.5*(ticks->horiz.nDigits+2.4);  /* maximum distance x label
1648                                            can project past xmin, xmax */
1649 
1650   if (((vf&TICK_L)&&(vf&TICK_OUT)) || (vf&TICK_C))
1651     dxmin= ticks->vert.tickOff+vlen;
1652   if (((vf&TICK_U)&&(vf&TICK_OUT)) || (vf&TICK_C))
1653     dxmax= ticks->vert.tickOff+vlen;
1654   if (((hf&TICK_L)&&(hf&TICK_OUT)) || (hf&TICK_C))
1655     dymin= ticks->horiz.tickOff+hlen;
1656   if (((hf&TICK_U)&&(hf&TICK_OUT)) || (hf&TICK_C))
1657     dymax= ticks->horiz.tickOff+hlen;
1658 
1659   if (vf & LABEL_L) xmin-= ticks->vert.labelOff+cx;
1660   else if ((hf&(LABEL_L|LABEL_U)) && hx>dxmin) xmin-= hx;
1661   else xmin-= dxmin;
1662   if (vf & LABEL_U) xmax+= ticks->vert.labelOff+cx;
1663   else if ((hf&(LABEL_L|LABEL_U)) && hx>dxmax) xmax+= hx;
1664   else xmax+= dxmax;
1665 
1666   if (hf & LABEL_L) ymin-= ticks->horiz.labelOff+2.0*cy;
1667   else if ((vf&(LABEL_L|LABEL_U)) && 0.5*cy>dymin) ymin-= 0.5*cy;
1668   else ymin-= dymin;
1669   if (hf & LABEL_U) xmax+= ticks->horiz.labelOff+2.0*cy;
1670   else if ((vf&(LABEL_L|LABEL_U)) && 0.5*cy>dymax) ymax+= 0.5*cy;
1671   else ymax+= dymax;
1672 
1673   if (vf & (TICK_L|TICK_U)) {
1674     xmin-= 0.5*ticks->vert.tickStyle.width*DEFAULT_LINE_WIDTH;
1675     xmax+= 0.5*ticks->vert.tickStyle.width*DEFAULT_LINE_WIDTH;
1676   }
1677   if (hf & (TICK_L|TICK_U)) {
1678     ymin-= 0.5*ticks->horiz.tickStyle.width*DEFAULT_LINE_WIDTH;
1679     ymax+= 0.5*ticks->horiz.tickStyle.width*DEFAULT_LINE_WIDTH;
1680   }
1681 
1682   box->xmin= xmin;
1683   box->xmax= xmax;
1684   box->ymin= ymin;
1685   box->ymax= ymax;
1686 
1687   /* Finally, swallow overflow boxes, assuming 22 characters max */
1688   overflow.xmin= ticks->horiz.xOver;
1689   overflow.ymin= ticks->horiz.yOver-ticks->horiz.textStyle.height*0.2;
1690   overflow.xmax= overflow.xmin+ticks->horiz.textStyle.height*(0.6*22.0);
1691   overflow.ymax= overflow.ymin+ticks->horiz.textStyle.height;
1692   GpSwallow(box, &overflow);
1693   overflow.xmin= ticks->vert.xOver;
1694   overflow.ymin= ticks->vert.yOver-ticks->vert.textStyle.height*0.2;
1695   overflow.xmax= overflow.xmin+ticks->vert.textStyle.height*(0.6*22.0);
1696   overflow.ymax= overflow.ymin+ticks->vert.textStyle.height;
1697   GpSwallow(box, &overflow);
1698 }
1699 
GdNewSystem(GpBox * viewport,GaTickStyle * ticks)1700 int GdNewSystem(GpBox *viewport, GaTickStyle *ticks)
1701 {
1702   GeSystem *sys;
1703   int sysIndex;
1704 
1705   if (!currentDr) return -1;
1706 
1707   /* Adding a new system clears the drawing */
1708   if (currentDr->cleared!=2) ClearDrawing(currentDr);
1709   sysIndex= currentDr->nSystems+1;
1710 
1711   sys= p_malloc(sizeof(GeSystem));
1712   if (!sys) return -1;
1713   sys->el.ops= opTables + E_SYSTEM;
1714   if (gistD.legend) {
1715     sys->el.legend= Copy1(gistD.legend, strlen(gistD.legend)+1);
1716     if (!sys->el.legend) { p_free(sys);  return -1; }
1717   } else sys->el.legend= 0;
1718   sys->el.hidden= gistD.hidden;
1719 
1720   if (sysIndex>1) {
1721     GdElement *prev= currentDr->systems->el.prev;
1722     prev->next= &sys->el;
1723     sys->el.prev= prev;
1724     sys->el.next= &currentDr->systems->el;
1725     currentDr->systems->el.prev= &sys->el;
1726   } else {
1727     sys->el.prev= sys->el.next= &sys->el;
1728     currentDr->systems= sys;
1729   }
1730   sys->el.number= -1;
1731   currentDr->nSystems++;
1732   sys->rescan= 0;
1733   sys->unscanned= -1;
1734 
1735   GuessBox(&sys->el.box, viewport, ticks);
1736 
1737   if (viewport->xmin<viewport->xmax) {
1738     sys->trans.viewport.xmin= viewport->xmin;
1739     sys->trans.viewport.xmax= viewport->xmax;
1740   } else {
1741     sys->trans.viewport.xmin= viewport->xmax;
1742     sys->trans.viewport.xmax= viewport->xmin;
1743   }
1744   if (viewport->ymin<viewport->ymax) {
1745     sys->trans.viewport.ymin= viewport->ymin;
1746     sys->trans.viewport.ymax= viewport->ymax;
1747   } else {
1748     sys->trans.viewport.ymin= viewport->ymax;
1749     sys->trans.viewport.ymax= viewport->ymin;
1750   }
1751   sys->trans.window.xmin= sys->trans.window.ymin= 0.0;
1752   sys->trans.window.xmax= sys->trans.window.ymax= 1.0;
1753   sys->ticks= *ticks;
1754   sys->flags= D_XMIN | D_XMAX | D_YMIN | D_YMAX;
1755   sys->elements= 0;
1756   sys->savedWindow.xmin= sys->savedWindow.ymin= 0.0;
1757   sys->savedWindow.xmax= sys->savedWindow.ymax= 1.0;
1758   sys->savedFlags= D_XMIN | D_XMAX | D_YMIN | D_YMAX;
1759 
1760   sys->xtick= sys->ytick= 0;
1761   sys->xlabel= sys->ylabel= 0;
1762 
1763   GdSetSystem(sysIndex);
1764   return sysIndex;
1765 }
1766 
GdGetSystem(void)1767 int GdGetSystem(void)
1768 {
1769   GdElement *sys, *sys0;
1770   int sysIndex= 0;
1771   if (!currentDr) return -1;
1772   if (!currentDr->systems || !currentSy) return 0;
1773 
1774   /* ClearDrawing sets currentSy-- must not be pending */
1775   if (currentDr->cleared==1) ClearDrawing(currentDr);
1776 
1777   sys= sys0= (GdElement *)currentDr->systems;
1778   for (sysIndex=1 ; sys!=&currentSy->el ; sysIndex++) {
1779     sys= sys->next;
1780     if (sys==sys0) return -2;
1781   }
1782 
1783   return sysIndex;
1784 }
1785 
GdSetSystem(int sysIndex)1786 int GdSetSystem(int sysIndex)
1787 {
1788   GeSystem *sys;
1789   GdElement *sys0;
1790   if (!currentDr || !currentDr->systems) return E_NONE;
1791 
1792   /* ClearDrawing sets currentSy-- must not be pending */
1793   if (currentDr->cleared==1) ClearDrawing(currentDr);
1794 
1795   currentEl= 0;
1796   currentCn= -1;
1797   if (sysIndex<1) {  /* Set current system to none */
1798     currentSy= 0;
1799     gistD.trans.viewport.xmin= gistD.trans.viewport.xmax=
1800       gistD.trans.viewport.ymin= gistD.trans.viewport.ymax= 0.0;
1801     gistD.flags= 0;
1802     return E_NONE;
1803   }
1804 
1805   sys= currentDr->systems;
1806   sys0= &sys->el;
1807   while (--sysIndex && sys->el.next!=sys0)
1808     sys= (GeSystem *)sys->el.next;
1809   if (sysIndex>0) return E_NONE;
1810 
1811   currentSy= sys;
1812   gistD.hidden= sys->el.hidden;
1813   gistD.legend= sys->el.legend;
1814   gistD.ticks= sys->ticks;
1815   gistD.trans.viewport= sys->trans.viewport;
1816   if (GdGetLimits()) {
1817     SystemKill(sys);
1818     return E_NONE;
1819   }
1820   return E_SYSTEM;
1821 }
1822 
GdSetElement(int elIndex)1823 int GdSetElement(int elIndex)
1824 {
1825   GdElement *el, *el0;
1826   if (!currentDr) return E_NONE;
1827 
1828   el= currentSy? currentSy->elements : currentDr->elements;
1829 
1830   if (elIndex<0 || !el) {   /* set current element to none */
1831     currentEl= 0;
1832     currentCn= -1;
1833     return E_NONE;
1834   }
1835 
1836   el0= el;
1837   while (elIndex-- && el->next!=el0) el= el->next;
1838   if (elIndex>=0) return E_NONE;
1839 
1840   currentEl= el;
1841   currentCn= -1;
1842   return el->ops->GetProps(el);
1843 }
1844 
NextConCurve(GdElement * el)1845 static GdElement *NextConCurve(GdElement *el)
1846 {
1847   GeContours *con= (GeContours *)currentEl;
1848   GdElement *el0= &con->groups[currentCn]->el;
1849   if (!el) el= el0;
1850   else if (el->next==el0) el= 0;
1851   else el= el->next;
1852   return el;
1853 }
1854 
GdSetContour(int levIndex)1855 int GdSetContour(int levIndex)
1856 {
1857   GeContours *con;
1858   GdElement *el;
1859   if (!currentDr || !currentEl || currentEl->ops->type!=E_CONTOURS)
1860     return E_NONE;
1861   con= (GeContours *)currentEl;
1862   if (levIndex>=con->nLevels) return E_NONE;
1863   if (levIndex<0) {
1864     currentCn= -1;
1865     return E_NONE;
1866   }
1867   currentCn= levIndex;
1868   el= NextConCurve((GdElement *)0);
1869   if (el) LinesGet(el);
1870   else ContoursGet(con);
1871   return E_LINES;
1872 }
1873 
GeFindIndex(int id,GeSystem * sys)1874 static int GeFindIndex(int id, GeSystem *sys)
1875 {
1876   int elIndex;
1877   GdElement *el, *el0;
1878   if (!currentDr) return -1;
1879 
1880   el= sys? sys->elements : currentDr->elements;
1881   if (!el) return -1;
1882 
1883   el0= el;
1884   elIndex= 0;
1885   while (el->number != id) {
1886     if (el->next==el0) return -1;
1887     el= el->next;
1888     elIndex++;
1889   }
1890 
1891   return elIndex;
1892 }
1893 
GdFindIndex(int id)1894 int GdFindIndex(int id)
1895 {
1896   return GeFindIndex(id, currentSy);
1897 }
1898 
GdFindSystem(int id)1899 int GdFindSystem(int id)
1900 {
1901   int sysIndex;
1902   GeSystem *sys;
1903   GdElement *sys0;
1904   if (!currentDr) return -1;
1905 
1906   if (GeFindIndex(id, 0)>=0) return 0;
1907   sys= currentDr->systems;
1908   if (!sys) return -1;
1909   sys0= &sys->el;
1910   sysIndex= 1;
1911   while (GeFindIndex(id, sys)<0) {
1912     if (sys->el.next==sys0) return -1;
1913     sys= (GeSystem *)sys->el.next;
1914     sysIndex++;
1915   }
1916   return sysIndex;
1917 }
1918 
GdAltTick(GaAltTicks * xtick,GaAltLabel * xlabel,GaAltTicks * ytick,GaAltLabel * ylabel)1919 int GdAltTick(GaAltTicks *xtick, GaAltLabel *xlabel,
1920               GaAltTicks *ytick, GaAltLabel *ylabel)
1921 {
1922   if (!currentDr || !currentSy) return 1;
1923   if (xtick) currentSy->xtick= xtick;
1924   if (ytick) currentSy->ytick= ytick;
1925   if (xlabel) currentSy->xlabel= xlabel;
1926   if (ylabel) currentSy->ylabel= ylabel;
1927   return 0;
1928 }
1929 
1930 /* ------------------------------------------------------------------------ */
1931 
GdEdit(int xyzChanged)1932 int GdEdit(int xyzChanged)
1933 {
1934   GdElement *el= currentEl;
1935   if (!currentDr || !el) return 1;
1936 
1937   /* Changing linestyles or most other simple changes may incur damage
1938      in a way that is very difficult to anticipate, hence, must call
1939      Damage here.  On the other hand, the elements need to be rescanned
1940      only if CHANGE_XY or CHANGE_Z has been set, so only set rescan
1941      in this case.  If only linestyles and such have been changed, GdScan
1942      will not call Damage again, although if they have it will-- hence, if
1943      you are changing both coordinates and linestyles, there is no way to
1944      avoid "double damage".  */
1945   Damage(currentSy, el);
1946   if (currentSy && xyzChanged) currentSy->rescan= 1;
1947 
1948   if (currentCn>=0) {
1949     el= NextConCurve((GdElement *)0);
1950     if (el) {
1951       /* legend only changes on first piece of contour */
1952       el->legend= gistD.legend;
1953       Gd_LinesSubSet(el);
1954       /* other line properties propagate to all contour pieces
1955          -- but NEVER attempt to change the (x,y) values */
1956       while ((el= NextConCurve(el))) Gd_LinesSubSet(el);
1957     }
1958     return 0;
1959   }
1960   return el->ops->SetProps(el, xyzChanged);
1961 }
1962 
GdRemove(void)1963 int GdRemove(void)
1964 {
1965   GdElement *el= currentEl;
1966   if (!currentDr || !el || currentCn>=0) return 1;
1967 
1968   /* Damage alert must take place here-- unfortunately, if this remove
1969      changes extreme values, a second call to Damage will be made in
1970      GdScan.  Hopefully, GdRemove is a rare enough operation that this
1971      inefficiency is negligible.  */
1972   Damage(currentSy, el);
1973 
1974   if (currentSy) {
1975     GdElement *prev= el->prev;
1976     if (el==prev) {
1977       currentSy->unscanned= -1;
1978       currentSy->rescan= 0;
1979       currentSy->el.number= -1;
1980     } else {
1981       if (el->number==currentSy->unscanned) {
1982         if (el->next != currentSy->elements)
1983           currentSy->unscanned= el->next->number;
1984         else currentSy->unscanned= -1;
1985       }
1986       if (el->number<currentSy->unscanned && !el->hidden)
1987         currentSy->rescan= 1;
1988       if (el->number==currentSy->el.number)
1989         currentSy->el.number= prev->number;
1990     }
1991   }
1992 
1993   if (currentSy && el==currentSy->elements) {
1994     if (el->next==el) currentSy->elements= 0;
1995     else currentSy->elements= el->next;
1996   } else if (el==currentDr->elements) {
1997     if (el->next==el) currentDr->elements= 0;
1998     else currentDr->elements= el->next;
1999   }
2000 
2001   el->ops->Kill(el);
2002   currentEl= 0;
2003   return 0;
2004 }
2005 
GdGetLimits(void)2006 int GdGetLimits(void)
2007 {
2008   if (!currentDr || !currentSy) return 1;
2009   if ((currentSy->rescan || currentSy->unscanned>=0)
2010       && GdScan(currentSy)) return 1;  /* memory manager failure */
2011   gistD.trans.window= currentSy->trans.window;
2012   gistD.flags= currentSy->flags;
2013 
2014   if (gistD.flags & D_LOGX) {
2015     gistD.limits.xmin= exp10(gistD.trans.window.xmin);
2016     gistD.limits.xmax= exp10(gistD.trans.window.xmax);
2017   } else {
2018     gistD.limits.xmin= gistD.trans.window.xmin;
2019     gistD.limits.xmax= gistD.trans.window.xmax;
2020   }
2021   if (gistD.flags & D_LOGY) {
2022     gistD.limits.ymin= exp10(gistD.trans.window.ymin);
2023     gistD.limits.ymax= exp10(gistD.trans.window.ymax);
2024   } else {
2025     gistD.limits.ymin= gistD.trans.window.ymin;
2026     gistD.limits.ymax= gistD.trans.window.ymax;
2027   }
2028   return 0;
2029 }
2030 
GdSetLimits(void)2031 int GdSetLimits(void)
2032 {
2033   int flags, rescan;
2034   if (!currentDr || !currentSy) return 1;
2035 
2036   if (gistD.flags & D_LOGX) {
2037     gistD.trans.window.xmin= SAFELOG(gistD.limits.xmin);
2038     gistD.trans.window.xmax= SAFELOG(gistD.limits.xmax);
2039   } else {
2040     gistD.trans.window.xmin= gistD.limits.xmin;
2041     gistD.trans.window.xmax= gistD.limits.xmax;
2042   }
2043   if (gistD.flags & D_LOGY) {
2044     gistD.trans.window.ymin= SAFELOG(gistD.limits.ymin);
2045     gistD.trans.window.ymax= SAFELOG(gistD.limits.ymax);
2046   } else {
2047     gistD.trans.window.ymin= gistD.limits.ymin;
2048     gistD.trans.window.ymax= gistD.limits.ymax;
2049   }
2050 
2051   flags= currentSy->flags;
2052   currentSy->flags= gistD.flags;
2053 
2054   /* Normally, setting the limits damages the entire system.
2055      However, would like to allow a special case for fixing limits to
2056      their existing extreme values.  */
2057   rescan= 1;
2058   if ( ! ( (flags^gistD.flags) & (~(D_XMIN|D_XMAX|D_YMIN|D_YMAX)) ) ) {
2059     if (((flags&D_XMIN)==(gistD.flags&D_XMIN) || (gistD.flags&D_XMIN)==0) &&
2060         ((flags&D_XMAX)==(gistD.flags&D_XMAX) || (gistD.flags&D_XMAX)==0) &&
2061         ((flags&D_YMIN)==(gistD.flags&D_YMIN) || (gistD.flags&D_YMIN)==0) &&
2062         ((flags&D_YMAX)==(gistD.flags&D_YMAX) || (gistD.flags&D_YMAX)==0)) {
2063       GpBox *w= &currentSy->trans.window;
2064       if (w->xmin==gistD.trans.window.xmin &&
2065           w->xmax==gistD.trans.window.xmax &&
2066           w->ymin==gistD.trans.window.ymin &&
2067           w->ymax==gistD.trans.window.ymax)
2068         rescan= 0;
2069     }
2070   }
2071   currentSy->trans.window= gistD.trans.window;
2072   currentSy->rescan|= rescan;
2073 
2074   /* damage alert takes place in GdScan just before rendering */
2075   return 0;
2076 }
2077 
GdSaveLimits(int resetZoomed)2078 int GdSaveLimits(int resetZoomed)
2079 {
2080   if (!currentDr || !currentSy) return 1;
2081   currentSy->savedWindow= currentSy->trans.window;
2082   currentSy->savedFlags= currentSy->flags;
2083   if (resetZoomed) currentSy->savedFlags&= ~D_ZOOMED;
2084   return 0;
2085 }
2086 
GdRevertLimits(int ifZoomed)2087 int GdRevertLimits(int ifZoomed)
2088 {
2089   if (!currentDr || !currentSy ||
2090       (ifZoomed && !(currentSy->flags&D_ZOOMED))) return 1;
2091   if (currentSy->savedFlags!=currentSy->flags ||
2092       currentSy->savedWindow.xmin!=currentSy->trans.window.xmin ||
2093       currentSy->savedWindow.xmax!=currentSy->trans.window.xmax ||
2094       currentSy->savedWindow.ymin!=currentSy->trans.window.ymin ||
2095       currentSy->savedWindow.ymax!=currentSy->trans.window.ymax) {
2096     currentSy->trans.window= currentSy->savedWindow;
2097     currentSy->flags= currentSy->savedFlags;
2098     currentSy->rescan= 1;
2099   }
2100   return 0;
2101 }
2102 
GdSetPort(void)2103 int GdSetPort(void)
2104 {
2105   GpBox *v, oldBox;
2106   if (!currentDr || !currentSy) return 1;
2107 
2108   currentSy->el.hidden= gistD.hidden;
2109   /*
2110   if (gistD.legend) {
2111     currentSy->el.legend= Copy1(gistD.legend, strlen(gistD.legend)+1);
2112   }
2113   */
2114 
2115   /* First, damage current coordinate system box.  */
2116   Damage(currentSy, (GdElement *)0);
2117 
2118   /* Save old box, set new ticks, viewport, and correponding box */
2119   oldBox= currentSy->el.box;
2120   v= &currentSy->trans.viewport;
2121   currentSy->ticks= gistD.ticks;
2122   *v= gistD.trans.viewport;
2123   GuessBox(&currentSy->el.box, &gistD.trans.viewport, &gistD.ticks);
2124 
2125   /* Since stacking order hasn't changed, new box must be damaged
2126      if it is not contained in the old box.  */
2127   v= &currentSy->el.box;
2128   if (v->xmin<oldBox.xmin || v->xmax>oldBox.xmax ||
2129       v->ymin<oldBox.ymin || v->ymax>oldBox.ymax)
2130     Damage(currentSy, (GdElement *)0);
2131 
2132   return 0;
2133 }
2134 
GdClearSystem(void)2135 GpBox *GdClearSystem(void)
2136 {
2137   GpBox *dBox;
2138   int n, nel;
2139   GeSystem *sys, *sys0;
2140   GdElement *el, *el0;
2141   /* Intended for use with animation... */
2142   if (!currentDr || !currentSy) return 0;
2143 
2144   Gd_KillRing(currentSy->elements);
2145   currentSy->elements= 0;
2146   currentSy->el.number= currentSy->unscanned= -1;
2147   currentSy->rescan= 0;
2148 
2149   sys0= currentDr->systems;
2150   el0= currentDr->elements;
2151   nel= -1;
2152   if ((sys= sys0)) do {
2153     if (sys==currentSy) continue;
2154     n= currentSy->el.number;
2155     if (n>nel) nel= n;
2156     sys= (GeSystem *)sys->el.next;
2157   } while (sys!=sys0);
2158   if ((el= el0)) do {
2159     n= el->number;
2160     if (n>nel) nel= n;
2161     el= el->next;
2162   } while (el!=el0);
2163   currentDr->nElements= nel+1;
2164 
2165   if (currentSy->flags & (D_XMIN|D_XMAX|D_YMIN|D_YMAX)) {
2166     /* Some extreme value set, damage whole box */
2167     dBox= &currentSy->el.box;
2168     Damage(currentSy, (GdElement *)0);
2169   } else {
2170     /* All limits fixed, damage only viewport */
2171     dBox= &currentSy->trans.viewport;
2172     Damage(currentSy, &currentSy->el);
2173   }
2174 
2175   return dBox;
2176 }
2177 
2178 /* ------------------------------------------------------------------------ */
2179