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(¤tDr->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= ¤tDr->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= ¤tDr->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= ¤tDr->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!=¤tSy->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= ¤tSy->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= ¤tSy->trans.viewport;
2121 currentSy->ticks= gistD.ticks;
2122 *v= gistD.trans.viewport;
2123 GuessBox(¤tSy->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= ¤tSy->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= ¤tSy->el.box;
2168 Damage(currentSy, (GdElement *)0);
2169 } else {
2170 /* All limits fixed, damage only viewport */
2171 dBox= ¤tSy->trans.viewport;
2172 Damage(currentSy, ¤tSy->el);
2173 }
2174
2175 return dBox;
2176 }
2177
2178 /* ------------------------------------------------------------------------ */
2179