4
* $Id: draw.c 685 2003-03-08 15:26:51Z travo $
6
* Implement display list portion of GIST C interface
9
/* Copyright (c) 1994. The Regents of the University of California.
10
All rights reserved. */
16
/* Generating default contour labels requires sprintf function */
21
extern double log10(double);
22
#define SAFELOG0 (-999.)
23
#define SAFELOG(x) ((x)>0? log10(x) : ((x)<0? log10(-(x)) : -999.))
26
extern GpReal GpNiceUnit(GpReal finest, int *base, int *power);
28
extern double floor(double);
29
extern double ceil(double);
31
extern double exp10(double);
33
# define exp10(x) pow(10.,x)
34
extern double pow(double,double);
36
#define LOG2 0.301029996
38
/* ------------------------------------------------------------------------ */
40
static Drauing *currentDr= 0; /* Drauing from GdNewDrawing or GdGetDrawing */
41
static GeSystem *currentSy; /* System from GdNewSystem or GdSetSystem */
42
static GdElement *currentEl; /* Element extracted with GdSetSystem, or
44
static int currentCn; /* level selected by GdGetContour */
46
/* Saved state information used by GdSetDrawing */
47
static Drauing *saveDr= 0;
48
static GeSystem *saveSy;
49
static GdElement *saveEl;
55
{7.5, 50., 1.2, 1.2, 4, 1, TICK_L|TICK_U|TICK_OUT|LABEL_L,
57
{12.*ONE_POINT, 8.*ONE_POINT, 5.*ONE_POINT, 3.*ONE_POINT, 2.*ONE_POINT},
58
{FG_COLOR, L_SOLID, 1.0},
59
{FG_COLOR, L_DOT, 1.0},
60
{FG_COLOR, T_HELVETICA, 14.*ONE_POINT,
61
TX_RIGHT, TH_NORMAL, TV_NORMAL, 1},
62
.425, .5-52.*ONE_POINT},
63
{7.5, 50., 1.2, 1.2, 3, 1, TICK_L|TICK_U|TICK_OUT|LABEL_L,
65
{12.*ONE_POINT, 8.*ONE_POINT, 5.*ONE_POINT, 3.*ONE_POINT, 2.*ONE_POINT},
66
{FG_COLOR, L_SOLID, 1.0},
67
{FG_COLOR, L_DOT, 1.0},
68
{FG_COLOR, T_HELVETICA, 14.*ONE_POINT,
69
TX_RIGHT, TH_NORMAL, TV_NORMAL, 1},
70
.25, .5-52.*ONE_POINT},
71
0, {FG_COLOR, L_SOLID, 1.0}
73
{{ 0.0, 1.0, 0.0, 1.0 }, { 0.0, 1.0, 0.0, 1.0 }},
74
D_XMIN | D_XMAX | D_YMIN | D_YMAX, /* flags */
75
{ 0.0, 1.0, 0.0, 1.0 }, /* limits */
78
0.0, 0.0, 0.0, 0.0, 0, 0, /* GdCells */
80
0, { 0, 0, 0, 0, 0, 0 }, 0, /* noCopy, mesh, region */
85
Drauing *gistDrawList= 0;
87
static void ClearDrawing(Drauing *drawing);
88
static void Damage(GeSystem *sys, GdElement *el);
89
static void SquareAdjust(GpReal *umin, GpReal *umax,
90
GpReal dv, int doMin, int doMax);
91
static void NiceAdjust(GpReal *umin, GpReal *umax, int isLog, int isMin);
92
static void EqAdjust(GpReal *umin, GpReal *umax);
93
static void EmptyAdjust(GpReal *umin, GpReal *umax, int doMin, int doMax);
94
static void EqualAdjust(GpReal *umin, GpReal *umax, int doMin, int doMax);
95
extern int Gd_DrawRing(void *elv, int xIsLog, int yIsLog,
96
GeSystem *sys, int t);
97
static void InitLegends(int contours, GeSystem *systems, GdElement *elements,
99
static void NextContours(void);
100
static int NextRing(void);
101
static int NextLegend(void);
102
static int BuildLegends(int more, int contours, GeSystem *systems,
103
GdElement *elements, GeLegendBox *lbox);
104
static int MemoryError(void);
105
static void *Copy1(const void *orig, long size);
106
static void *Copy2(void *x1, const void *orig1, const void *orig2, long size);
107
extern void Gd_ScanZ(long n, const GpReal *z, GpReal *zmin, GpReal *zmax);
108
static void ScanXY(long n, const GpReal *x, const GpReal *y, GpBox *extrema);
110
extern void Gd_NextMeshBlock(long *ii, long *jj, long len, long iMax,
111
int *reg, int region);
112
extern void Gd_MeshXYGet(void *vMeshEl);
113
static int AutoMarker(GaLineAttribs *dl, int number);
114
extern int Gd_MakeContours(GeContours *con);
115
static void GuessBox(GpBox *box, GpBox *viewport, GaTickStyle *ticks);
116
static GdElement *NextConCurve(GdElement *el);
117
static int GeFindIndex(int id, GeSystem *sys);
119
extern void Gd_KillRing(void *elv);
120
extern void Gd_KillMeshXY(void *vMeshEl);
122
static void (*DisjointKill)(void *el);
123
static void (*FilledKill)(void *el);
124
static void (*VectorsKill)(void *el);
125
static void (*ContoursKill)(void *el);
126
static void (*SystemKill)(void *el);
127
static int (*LinesGet)(void *el);
128
static int (*ContoursGet)(void *el);
129
extern void Gd_LinesSubSet(void *el);
130
static int (*SystemDraw)(void *el, int xIsLog, int yIsLog);
132
/* ------------------------------------------------------------------------ */
133
/* Set virtual function tables */
135
extern GdOpTable *GetDrawingOpTables(void); /* in draw0.c */
136
static GdOpTable *opTables= 0;
138
/* ------------------------------------------------------------------------ */
139
/* Constructor and destructor for Drauing declared in gist.h */
141
Drauing *GdNewDrawing(char *gsFile)
143
Drauing *drawing= p_malloc(sizeof(Drauing));
144
if (!drawing) return 0;
146
opTables= GetDrawingOpTables();
147
DisjointKill= opTables[E_DISJOINT].Kill;
148
FilledKill= opTables[E_FILLED].Kill;
149
VectorsKill= opTables[E_VECTORS].Kill;
150
ContoursKill= opTables[E_CONTOURS].Kill;
151
SystemKill= opTables[E_SYSTEM].Kill;
152
LinesGet= opTables[E_LINES].GetProps;
153
ContoursGet= opTables[E_CONTOURS].GetProps;
154
SystemDraw= opTables[E_SYSTEM].Draw;
157
drawing->next= gistDrawList;
158
gistDrawList= drawing;
160
drawing->nSystems= drawing->nElements= 0;
162
drawing->elements= 0;
164
drawing->damage.xmin= drawing->damage.xmax=
165
drawing->damage.ymin= drawing->damage.ymax= 0.0;
166
drawing->landscape= 0;
167
drawing->legends[0].nlines= drawing->legends[1].nlines= 0;
169
GdSetDrawing(drawing);
171
if (GdReadStyle(drawing, gsFile)) {
173
GdKillDrawing(drawing);
180
int GdLandscape(int landscape)
182
if (!currentDr) return 1;
183
if (landscape) landscape= 1;
184
if (currentDr->landscape!=landscape) {
185
currentDr->landscape= landscape;
186
GdDetach(currentDr, 0);
191
void GdKillDrawing(Drauing *drawing)
195
if (!drawing) return;
198
ClearDrawing(drawing);
199
Gd_KillRing(drawing->systems);
201
if (drawing==gistDrawList) gistDrawList= drawing->next;
203
Drauing *draw= gistDrawList;
204
while (draw->next!=drawing) draw= draw->next;
205
draw->next= drawing->next;
208
if (drawing==currentDr) currentDr= 0;
213
extern void GdKillSystems(void);
215
void GdKillSystems(void)
217
if (!currentDr) return;
218
ClearDrawing(currentDr);
219
Gd_KillRing(currentDr->systems);
220
currentDr->systems= 0;
221
currentDr->nSystems= 0;
224
int GdSetDrawing(Drauing *drawing)
226
int nMax, sysIndex, i;
229
if (!drawing) { /* swap current and saved state info */
230
Drauing *tmpDr= currentDr;
231
GeSystem *tmpSy= currentSy;
232
GdElement *tmpEl= currentEl;
233
int tmpCn= currentCn;
234
currentDr= saveDr; saveDr= tmpDr;
235
currentSy= saveSy; saveSy= tmpSy;
236
currentEl= saveEl; saveEl= tmpEl;
237
currentCn= saveCn; saveCn= tmpCn;
248
/* Make a reasonable guess at current system and element */
249
nMax= drawing->elements? drawing->elements->prev->number : -1;
250
sysIndex= drawing->nSystems? 1 : 0;
252
if ((sys= drawing->systems)) do {
254
if (sys->el.number>nMax) { nMax= sys->el.number; sysIndex= i; }
255
sys= (GeSystem *)sys->el.next;
256
} while (sys!=drawing->systems);
257
GdSetSystem(sysIndex);
259
if (sysIndex<1) currentSy= 0;
262
GdElement *el= currentSy? currentSy->elements : drawing->elements;
265
currentEl->ops->GetProps(currentEl);
275
int GdClear(Drauing *drawing)
277
if (!drawing) drawing= currentDr;
278
if (!drawing) return 1;
283
static void ClearDrawing(Drauing *drawing)
285
GeSystem *sys, *sys0= drawing->systems;
287
if ((sys= sys0)) do {
288
Gd_KillRing(sys->elements);
293
sys= (GeSystem *)sys->el.next;
296
Gd_KillRing(drawing->elements);
297
drawing->elements= 0;
298
drawing->nElements= 0;
299
drawing->nSystems= nSystems;
302
if (drawing==currentDr) {
303
currentSy= drawing->systems; /* as after GdSetDrawing */
308
/* Must detatch drawing from all engines, since even inactive ones
309
need to know that the drawing has been erased. */
310
GdDetach(drawing, (Engine *)0);
313
/* ------------------------------------------------------------------------ */
315
/* The GIST display list is called a "drawing". Any number of drawings
316
may exist, but only one may be active at a time.
318
The entire drawing is rendered on all active engines by calling
319
GdDraw(0). The drawing sequence is preceded by GpClear(0, CONDITIONALLY).
321
GdDraw(-1) is like GdDraw(0) except the drawing is damaged to force
322
all data to be rescanned as well.
324
GdDraw(1) draws only the changes since the previous GdDraw. Changes can
325
be either destructive or non-destructive:
327
If the engine->damage box is set, then some operation has
328
damaged that part of the drawing since the prior GdDraw
329
(such as changing a line style or plot limits). The damaged
330
section is cleared, then the display list is traversed, redrawing
331
all elements which may intersect the damaged box, clipped to
334
Then, any elements which were added since the prior GdDraw
335
(and which caused no damage like a change in limits) are
336
drawn as non-destructive updates.
340
static void Damage(GeSystem *sys, GdElement *el)
342
GpBox *box, adjustBox;
343
if (!currentDr) return;
346
/* If no element, damage the entire coordinate system, ticks and all */
349
/* If element is in a coordinate system, damage the whole viewport */
350
box= &sys->trans.viewport;
352
/* Elements not in a coordinate system already have NDC box--
353
but may need to adjust it to allow for projecting junk. */
354
el->ops->Margin(el, &adjustBox);
355
adjustBox.xmin+= el->box.xmin;
356
adjustBox.xmax+= el->box.xmax;
357
adjustBox.ymin+= el->box.ymin;
358
adjustBox.ymax+= el->box.ymax;
361
if (currentDr->damaged) {
362
GpSwallow(¤tDr->damage, box);
364
currentDr->damage= *box;
365
currentDr->damaged= 1;
369
static void SquareAdjust(GpReal *umin, GpReal *umax,
370
GpReal dv, int doMin, int doMax)
373
if (doMax) umax[0]= 0.5*(umin[0]+umax[0]+dv);
380
static void NiceAdjust(GpReal *umin, GpReal *umax, int isLog, int isMin)
382
GpReal un= *umin, ux= *umax;
383
GpReal unit, du= ux-un;
384
int base, power, reverted= 0;
387
/* revert to linear scale */
392
} else if (du>6.0 && isMin) {
397
unit= GpNiceUnit(du/3.0, &base, &power);
398
if (!isLog || reverted || unit>0.75) {
399
un= unit*floor(un/unit);
400
ux= unit*ceil(ux/unit);
406
/* subdecade log scale (sigh), use 2, 5, or 10 */
407
GpReal dn= floor(un+0.0001), dx= ceil(ux-0.0001);
408
if (un<dn+(LOG2-0.0001)) un= dn;
409
else if (un<dn+(0.9999-LOG2)) un= dn+LOG2;
410
else un= dn+(1.0-LOG2);
411
if (ux>dx-(LOG2+0.0001)) ux= dx;
412
else if (ux>dx-(1.0001-LOG2)) ux= dx-LOG2;
413
else ux= ux-(1.0-LOG2);
419
static void EqAdjust(GpReal *umin, GpReal *umax)
421
GpReal nudge= *umin>0.0? 0.001*(*umin) : -0.001*(*umin);
422
if (nudge==0.0) nudge= 1.0e-6;
427
static void EmptyAdjust(GpReal *umin, GpReal *umax, int doMin, int doMax)
430
if (doMax) { *umin= -1.0e-6; *umax= +1.0e-6; }
431
else if (*umax>0.0) *umin= 0.999*(*umax);
432
else if (*umax<0.0) *umin= 1.001*(*umax);
435
if (*umin>0.0) *umax= 1.001*(*umin);
436
else if (*umin<0.0) *umax= 0.999*(*umin);
438
} else if ((*umin)==(*umax)) {
439
EqAdjust(umin, umax);
443
static void EqualAdjust(GpReal *umin, GpReal *umax, int doMin, int doMax)
445
if (doMin && doMax) EqAdjust(umin, umax);
446
else EmptyAdjust(umin, umax, doMin, doMax);
449
int GdScan(GeSystem *sys)
451
int flags= sys->flags;
452
GpBox limits, tmp, *w= &sys->trans.window;
453
GpReal xmin= w->xmin, xmax= w->xmax, ymin= w->ymin, ymax= w->ymax;
455
GdElement *el, *el0= sys->elements;
456
int begin, damaged, first;
458
/* Handle case of no curves (if, e.g., all elements removed) */
460
EmptyAdjust(&w->xmin, &w->xmax, flags&D_XMIN, flags&D_XMAX);
461
EmptyAdjust(&w->ymin, &w->ymax, flags&D_YMIN, flags&D_YMAX);
465
/* Assure that limits are ordered even if window is not */
466
swapx= (xmin > xmax) && !(flags&(D_XMIN|D_XMAX));
467
swapy= (ymin > ymax) && !(flags&(D_YMIN|D_YMAX));
468
if (!swapx) { limits.xmin= xmin; limits.xmax= xmax; }
469
else { limits.xmin= xmax; limits.xmax= xmin; }
470
if (!swapy) { limits.ymin= ymin; limits.ymax= ymax; }
471
else { limits.ymin= ymax; limits.ymax= ymin; }
475
begin= sys->rescan? -1 : sys->unscanned;
477
/* Scan limits for each element */
482
if (el->number>=begin) {
483
/* Scan ensures log values present, sets box, scans xy values */
484
if (el->ops->Scan(el, flags, &tmp)) return 1; /* mem failure */
486
/* first non-hidden element gives first cut at limits */
490
/* subsequent elements may cause limits to be adjusted */
491
if (tmp.xmin<=tmp.xmax) {
492
if (tmp.xmin<limits.xmin) limits.xmin= tmp.xmin;
493
if (tmp.xmax>limits.xmax) limits.xmax= tmp.xmax;
495
if (tmp.ymin<=tmp.ymax) {
496
if (tmp.ymin<limits.ymin) limits.ymin= tmp.ymin;
497
if (tmp.ymax>limits.ymax) limits.ymax= tmp.ymax;
506
/* #1- adjust if min==max */
507
if (limits.xmin==limits.xmax)
508
EqualAdjust(&limits.xmin, &limits.xmax, flags&D_XMIN, flags&D_XMAX);
509
if (limits.ymin==limits.ymax)
510
EqualAdjust(&limits.ymin, &limits.ymax, flags&D_XMIN, flags&D_XMAX);
512
/* #2- adjust if log axis and minimum was SAFELOG(0) */
513
if ((flags & D_LOGX) && (flags & D_XMIN) && limits.xmin==SAFELOG0
514
&& limits.xmax>SAFELOG0+10.0) limits.xmin= limits.xmax-10.0;
515
if ((flags & D_LOGY) && (flags & D_YMIN) && limits.ymin==SAFELOG0
516
&& limits.ymax>SAFELOG0+10.0) limits.ymin= limits.ymax-10.0;
518
/* #3- adjust if square limits specified and not semi-logarithmic */
519
if ((flags & D_SQUARE) &&
520
!(((flags&D_LOGX)!=0) ^ ((flags&D_LOGY)!=0))) {
521
/* (Square axes don't make sense for semi-log scales) */
522
GpReal dx= limits.xmax-limits.xmin;
523
GpReal dy= limits.ymax-limits.ymin;
524
GpReal dydx= (sys->trans.viewport.ymax-sys->trans.viewport.ymin)/
525
(sys->trans.viewport.xmax-sys->trans.viewport.xmin);
526
/* Adjust y if either (1) dx>dy, or (2) x limits are both fixed
527
(NB- SquareAdjust is a noop if both limits fixed) */
528
if ((dx*dydx>dy && (flags&(D_YMIN|D_YMAX))) ||
529
!(flags&(D_XMIN|D_XMAX)))
530
SquareAdjust(&limits.ymin, &limits.ymax, dx*dydx,
531
flags&D_YMIN, flags&D_YMAX);
533
SquareAdjust(&limits.xmin, &limits.xmax, dy/dydx,
534
flags&D_XMIN, flags&D_XMAX);
537
/* #4- adjust if nice limits specified */
538
if (flags & D_NICE) {
539
NiceAdjust(&limits.xmin, &limits.xmax, flags&D_LOGX, flags&D_XMIN);
540
NiceAdjust(&limits.ymin, &limits.ymax, flags&D_LOGY, flags&D_YMIN);
544
GpReal tmp= limits.xmin; limits.xmin= limits.xmax; limits.xmax= tmp;
547
GpReal tmp= limits.ymin; limits.ymin= limits.ymax; limits.ymax= tmp;
549
if (damaged || limits.xmin!=xmin || limits.xmax!=xmax ||
550
limits.ymin!=ymin || limits.ymax!=ymax)
551
Damage(sys, (GdElement *)0);
552
w->xmin= limits.xmin;
553
w->xmax= limits.xmax;
554
w->ymin= limits.ymin;
555
w->ymax= limits.ymax;
563
int Gd_DrawRing(void *elv, int xIsLog, int yIsLog, GeSystem *sys, int t)
565
GdElement *el0, *el= elv;
566
GpBox adjustBox, *box;
567
int value= 0, drawIt= t;
572
el->ops->Margin(el, &adjustBox);
573
adjustBox.xmin+= el->box.xmin;
574
adjustBox.xmax+= el->box.xmax;
575
adjustBox.ymin+= el->box.ymin;
576
adjustBox.ymax+= el->box.ymax;
579
box= &sys->trans.viewport;
581
drawIt= GdBeginEl(box, el->number);
583
if (drawIt) value|= el->ops->Draw(el, xIsLog, yIsLog);
590
static GpTransform unitTrans= { {0., 2., 0., 2.}, {0., 2., 0., 2.} };
592
int GdDraw(int changesOnly)
599
if (!currentDr) return 1;
601
if (changesOnly==-1) {
606
/* Take care of conditional clear */
607
if (currentDr->cleared==1) {
608
if (changesOnly) return 0;
609
else ClearDrawing(currentDr);
611
if (!changesOnly || currentDr->cleared) {
612
GpClear(0, CONDITIONALLY);
613
currentDr->cleared= 0;
616
/* Check if any coordinate systems need to be rescanned */
617
if (currentDr->systems) {
619
GeSystem *sys, *sys0;
620
sys= sys0= currentDr->systems;
622
if (rescan) sys->rescan= 1;
623
changed= (sys->rescan || sys->unscanned>=0);
624
if (changed) changesOnly= 0;
625
if (changed && GdScan(sys)) return 1; /* memory manager failure */
626
sys= (GeSystem *)sys->el.next;
630
/* Give engines a chance to prepare for a drawing */
631
if (currentDr->damaged) {
632
damage= ¤tDr->damage;
633
currentDr->damaged= 0;
637
/* GdBeginDr returns 1 if any active engine has been cleared or
638
partially cleared. */
639
if (!GdBeginDr(currentDr, damage, currentDr->landscape) && changesOnly)
642
/* Do coordinate systems */
643
if (currentDr->systems) {
644
GeSystem *sys, *sys0;
645
sys= sys0= currentDr->systems;
648
value|= SystemDraw(sys, systemCounter, 0);
650
sys= (GeSystem *)sys->el.next;
654
/* Do elements outside of coordinate systems */
655
GpSetTrans(&unitTrans);
657
value|= Gd_DrawRing(currentDr->elements, 0, 0, (GeSystem *)0, 0);
659
/* Give engines a chance to clean up after a drawing */
665
/* ------------------------------------------------------------------------ */
666
/* Legend routines */
668
int GdLegendBox(int which, GpReal x, GpReal y, GpReal dx, GpReal dy,
669
const GpTextAttribs *t, int nchars, int nlines, int nwrap)
672
if (!currentDr || nchars<0) return 1;
673
lbox= currentDr->legends;
675
lbox->x= x; lbox->y= y;
676
lbox->dx= dx; lbox->dy= dy;
678
lbox->nchars= nchars;
679
lbox->nlines= nlines;
684
static char *legendText= 0;
685
static long lenLegends, maxLegends= 0;
687
static int nRemaining, curWrap;
688
static char *curLegend;
689
static int curMarker= 0;
691
static int doingContours, levelCurve, nLevels;
692
static GdElement *curElement, *cur0Element, *drElements, *curCon, *cur0Con;
693
static GeSystem *curSystem, *cur0System;
694
static GpReal *levelValue;
695
static GeLines **levelGroup;
696
static char levelLegend[32];
698
static void InitLegends(int contours, GeSystem *systems, GdElement *elements,
701
doingContours= levelCurve= contours;
702
if (doingContours) curCon= 0;
704
curSystem= cur0System= systems;
705
drElements= elements;
710
if (size>maxLegends) {
711
if (legendText) p_free(legendText);
712
legendText= p_malloc((long)size);
716
static void NextContours(void)
719
/* Set up for the ring of level curves */
720
GeContours *con= (GeContours *)curCon;
721
nLevels= con->nLevels;
722
levelValue= con->levels;
723
levelGroup= con->groups;
726
while (nLevels && !levelGroup[0]) {
734
if (nLevels>0) curElement= (GdElement *)levelGroup[0];
742
curCon= curCon->next;
743
if (curCon==cur0Con) curCon= 0;
748
if (curCon->ops->type==E_CONTOURS && !curCon->hidden) {
749
/* Set up for contour element itself-- terminates immediately */
751
cur0Element= curElement->next;
754
curCon= curCon->next;
755
} while (curCon!=cur0Con);
759
curCon= cur0Con= curSystem->elements;
760
curSystem= (GeSystem *)curSystem->el.next;
761
if (curSystem==cur0System) curSystem= 0;
762
} else if (drElements) {
763
curCon= cur0Con= drElements;
771
static int NextRing(void)
775
if (!curElement) return 0;
776
} else if (curSystem) {
777
curElement= cur0Element= curSystem->elements;
778
curSystem= (GeSystem *)curSystem->el.next;
779
if (curSystem==cur0System) curSystem= 0;
780
} else if (drElements) {
781
curElement= cur0Element= drElements;
789
static int specialMarks[5]= { '.', '+', '*', 'o', 'x' };
791
static int NextLegend(void)
797
if (!curElement->hidden) {
798
int type= curElement->ops->type;
799
if (curElement->legend) curLegend= curElement->legend;
800
else if (levelCurve) {
801
/* automatically generate level curve legend if not supplied */
802
curLegend= levelLegend;
803
sprintf(curLegend, "\001: %.4g", *levelValue);
806
nRemaining= strlen(curLegend);
808
if ((type==E_LINES || type==E_CONTOURS) && curLegend[0]=='\001') {
809
/* insert marker into E_LINES legend if so directed */
810
curMarker= type==E_LINES? ((GeLines *)curElement)->m.type :
811
((GeContours *)curElement)->m.type;
812
if (curMarker>=1 && curMarker<=5)
813
curMarker= specialMarks[curMarker-1];
814
else if (curMarker<' ' || curMarker>='\177')
824
} while (nLevels && !levelGroup[0]);
825
if (nLevels>0) curElement= (GdElement *)levelGroup[0];
828
curElement= curElement->next;
829
if (curElement==cur0Element) curElement= 0;
831
if (curLegend) return 1;
833
} while (NextRing());
837
static int BuildLegends(int more, int contours, GeSystem *systems,
838
GdElement *elements, GeLegendBox *lbox)
841
int nlines= lbox->nlines;
842
int nchars= lbox->nchars;
843
int nwrap= lbox->nwrap;
848
if (nlines<=0 || nchars<=0) return 0;
849
InitLegends(contours, systems, elements, (nchars+1)*nlines);
850
if (!legendText) return 0;
853
for ( ; ; nlines--) {
854
if (!curLegend && !NextLegend()) { more= 0; break; }
855
if (nlines<=0) { more= !more; break; }
856
if (firstLine) firstLine= 0;
857
else legendText[lenLegends++]= '\n';
858
nc= nRemaining>nchars? nchars : nRemaining;
859
strncpy(legendText+lenLegends, curLegend, nc);
861
legendText[lenLegends]= (char)curMarker;
866
if (nRemaining>0 && curWrap++<nwrap) curLegend+= nc;
867
else { curLegend= 0; curMarker= 0; }
870
legendText[lenLegends]= '\0';
874
int GdDrawLegends(Engine *engine)
879
if (!currentDr) return 1;
881
if (engine) GpPreempt(engine);
883
for (type=0 ; type<2 ; type++) {
884
lbox= ¤tDr->legends[type];
887
gistA.t= lbox->textStyle;
888
GpSetTrans(&unitTrans);
890
if (lbox->nlines <= 0) continue;
892
more= BuildLegends(more, type, currentDr->systems, currentDr->elements,
896
if (engine) GpPreempt(0);
899
if (lenLegends>0) GpText(x, y, legendText);
900
if (!more || (lbox->dx==0.0 && lbox->dy==0.0)) break;
906
if (engine) GpPreempt(0);
910
/* ------------------------------------------------------------------------ */
911
/* Utility routines */
913
static int MemoryError(void)
916
strcpy(gistError, "memory manager failed in Gd function");
918
strcpy(gistError, "currentDr not set in Gd function");
922
static void *Copy1(const void *orig, long size)
925
if (size<=0) return 0;
927
if (!px) MemoryError();
928
else if (orig) memcpy(px, orig, size);
932
static void *Copy2(void *x1, const void *orig1, const void *orig2, long size)
934
void *x2, **x1p= (void **)x1;
935
*x1p= Copy1(orig1, size);
937
x2= Copy1(orig2, size);
938
if (!x2) { p_free(*x1p); *x1p= 0; }
942
void Gd_ScanZ(long n, const GpReal *z, GpReal *zmin, GpReal *zmax)
947
for (i=1 ; i<n ; i++) {
948
if (z[i]<zn) zn= z[i];
949
else if (z[i]>zx) zx= z[i];
955
static void ScanXY(long n, const GpReal *x, const GpReal *y, GpBox *extrema)
957
Gd_ScanZ(n, x, &extrema->xmin, &extrema->xmax);
958
Gd_ScanZ(n, y, &extrema->ymin, &extrema->ymax);
961
void GeAddElement(int type, GdElement *element)
964
Drauing *drawing= currentDr;
967
if (drawing->cleared==1) ClearDrawing(drawing);
970
old= sys? sys->elements : drawing->elements;
971
if (!old) { /* this is first element */
972
if (sys) sys->elements= element;
973
else drawing->elements= element;
974
element->prev= element->next= element;
975
} else { /* insert element at end of ring */
976
element->prev= old->prev;
978
old->prev= element->prev->next= element;
980
element->ops= opTables + type;
981
element->hidden= gistD.hidden;
983
element->legend= Copy1(gistD.legend, strlen(gistD.legend)+1);
984
/* sigh. ignore memory error here */
988
element->number= drawing->nElements++;
989
/* System nust always have number of its largest element for
990
GdBeginSy to work properly */
991
if (sys) sys->el.number= element->number;
992
else Damage((GeSystem *)0, element);
995
void Gd_NextMeshBlock(long *ii, long *jj, long len, long iMax,
996
int *reg, int region)
997
{ /* Find next contiguous run of mesh points in given region */
1001
for (j=i ; j<len ; j++)
1002
if (reg[j] || reg[j+1] || reg[j+iMax] || reg[j+iMax+1]) break;
1004
for (j=i+1 ; j<len ; j++)
1005
if (!reg[j] && !reg[j+1] && !reg[j+iMax] && !reg[j+iMax+1]) break;
1007
for (j=i ; j<len ; j++)
1008
if (reg[j]==region || reg[j+1]==region ||
1009
reg[j+iMax]==region || reg[j+iMax+1]==region) break;
1011
for (j=i+1 ; j<len ; j++)
1012
if (reg[j]!=region && reg[j+1]!=region &&
1013
reg[j+iMax]!=region && reg[j+iMax+1]!=region) break;
1019
long GeGetMesh(int noCopy, GaQuadMesh *meshin, int region, void *vMeshEl)
1021
GeMesh *meshEl= vMeshEl;
1022
GaQuadMesh *mesh= &meshEl->mesh;
1023
GpBox *linBox= &meshEl->linBox;
1024
long iMax, jMax, i, j, len;
1027
if (currentDr->cleared==1) ClearDrawing(currentDr);
1029
/* retrieve mesh shape from meshin */
1030
mesh->iMax= iMax= meshin->iMax;
1031
mesh->jMax= jMax= meshin->jMax;
1034
mesh->reg= 0; /* set up for possible error return */
1035
mesh->triangle= 0; /* only needed by GdContours, so handled there */
1037
meshEl->xlog= meshEl->ylog= 0;
1038
meshEl->region= region;
1040
meshEl->noCopy= noCopy & (NOCOPY_MESH|NOCOPY_COLORS|NOCOPY_UV|NOCOPY_Z);
1041
if (noCopy&NOCOPY_MESH) {
1042
/* Just copy pointers to mesh arrays -- NOCOPY also means not
1043
to free the pointer later */
1047
/* Copy the mesh arrays themselves */
1048
mesh->y= Copy2(&mesh->x, meshin->x, meshin->y, sizeof(GpReal)*len);
1049
if (!mesh->y) { p_free(vMeshEl); return 0; }
1052
if ((noCopy&NOCOPY_MESH) && meshin->reg) {
1053
mesh->reg= reg= meshin->reg;
1054
meshEl->noCopy|= NOCOPY_REG;
1056
mesh->reg= reg= Copy1(meshin->reg, sizeof(int)*(len+iMax+1));
1057
if (!reg) { Gd_KillMeshXY(vMeshEl); p_free(vMeshEl); return 0; }
1060
/* Be sure region array is legal */
1061
for (i=0 ; i<iMax ; i++) reg[i]= 0;
1062
if (!meshin->reg) for (i=iMax ; i<len ; i++) reg[i]= 1;
1063
for (i=len ; i<len+iMax+1 ; i++) reg[i]= 0;
1064
for (i=0 ; i<len ; i+=iMax) reg[i]= 0;
1066
/* Scan mesh for extreme values */
1071
/* Use ScanXY on the longest contiguous run(s) of points */
1072
for (i=0 ; i<len ; ) {
1073
Gd_NextMeshBlock(&i, &j, len, iMax, reg, region);
1075
ScanXY(j-i, mesh->x+i, mesh->y+i, &box);
1076
if (first) { *linBox= box; first= 0; }
1077
else GpSwallow(linBox, &box);
1081
linBox->xmin= linBox->xmax= linBox->ymin= linBox->ymax= 0.0;
1084
ScanXY(len, mesh->x, mesh->y, linBox);
1086
if (!currentSy) meshEl->el.box= *linBox; /* for GeAddElement */
1088
/* copy mesh properties to gistD */
1089
Gd_MeshXYGet(vMeshEl);
1094
void GeMarkForScan(GdElement *el, GpBox *linBox)
1097
if (currentSy->unscanned<0) currentSy->unscanned= el->number;
1103
static int AutoMarker(GaLineAttribs *dl, int number)
1107
if (number>=26) number%= 26;
1108
dl->mPhase= 0.25*(0.5+p)*dl->mSpace;
1109
dl->rPhase= 0.25*(0.5+p)*dl->rSpace;
1113
/* ------------------------------------------------------------------------ */
1114
/* Constructors for drawing elements are public routines declared in gist.h */
1116
int GdLines(long n, const GpReal *px, const GpReal *py)
1119
if (n<=0) return -1;
1120
el= currentDr? p_malloc(sizeof(GeLines)) : 0;
1121
if (!el) return MemoryError();
1122
el->xlog= el->ylog= 0;
1124
/* make private copies of x and y arrays */
1125
el->y= Copy2(&el->x, px, py, sizeof(GpReal)*n);
1126
if (!el->y) { p_free(el); return -1; }
1129
/* scan for min and max of x and y arrays */
1130
ScanXY(n, px, py, &el->linBox);
1131
if (!currentSy) el->el.box= el->linBox; /* for GeAddElement */
1133
/* copy relevant attributes from gistA
1134
-- This must be done BEFORE GeAddElement, since the damage
1135
calculation depends on the Margins! */
1140
/* set base class members */
1141
GeAddElement(E_LINES, &el->el);
1142
if (gistA.m.type==0) el->m.type= AutoMarker(&el->dl, el->el.number);
1144
/* current box not set, mark as unscanned if in system */
1145
GeMarkForScan(&el->el, &el->linBox);
1147
/* copy properties to gistD */
1152
return el->el.number;
1155
int GdDisjoint(long n, const GpReal *px, const GpReal *py,
1156
const GpReal *qx, const GpReal *qy)
1160
if (n<=0) return -1;
1161
el= currentDr? p_malloc(sizeof(GeDisjoint)) : 0;
1162
if (!el) return MemoryError();
1163
el->el.next= el->el.prev= 0;
1164
el->xlog= el->ylog= el->xqlog= el->yqlog= 0;
1166
/* make private copies of x, y, xq, yq arrays */
1167
el->y= Copy2(&el->x, px, py, sizeof(GpReal)*n);
1168
if (!el->y) { p_free(el); return -1; }
1169
el->yq= Copy2(&el->xq, qx, qy, sizeof(GpReal)*n);
1170
if (!el->yq) { DisjointKill(el); return -1; }
1173
/* scan for min and max of x and y arrays */
1174
ScanXY(n, px, py, &box);
1175
ScanXY(n, qx, qy, &el->linBox);
1176
GpSwallow(&el->linBox, &box);
1177
if (!currentSy) el->el.box= el->linBox; /* for GeAddElement */
1179
/* copy relevant attributes from gistA
1180
-- This must be done BEFORE GeAddElement, since the damage
1181
calculation depends on the Margins! */
1184
/* set base class members */
1185
GeAddElement(E_DISJOINT, &el->el);
1187
/* current box not set, mark as unscanned if in system */
1188
GeMarkForScan(&el->el, &el->linBox);
1190
/* copy properties to gistD */
1197
return el->el.number;
1200
int GdText(GpReal x0, GpReal y0, const char *text, int toSys)
1202
GeText *el= currentDr? p_malloc(sizeof(GeText)) : 0;
1203
GeSystem *sys= currentSy;
1204
if (!el) return MemoryError();
1206
/* make private copy of text string */
1207
el->text= Copy1(text, strlen(text)+1);
1208
if (!el->text) { p_free(el); return -1; }
1212
/* Without some sort of common font metric, there is no way to
1213
know the box associated with text. Even with such a metric,
1214
the box would have to change with the coordinate transform,
1215
unlike any other element. For now, punt. */
1216
el->el.box.xmin= el->el.box.xmax= x0;
1217
el->el.box.ymin= el->el.box.ymax= y0;
1219
/* copy relevant attributes from gistA
1220
-- This must be done BEFORE GeAddElement, since the damage
1221
calculation depends on the Margins! */
1224
/* set base class members */
1225
if (currentDr->cleared==1) ClearDrawing(currentDr); /* else currentSy */
1226
if (!toSys) currentSy= 0; /* can be clobbered... */
1227
GeAddElement(E_TEXT, &el->el);
1228
if (currentSy && currentSy->unscanned<0)
1229
currentSy->unscanned= el->el.number;
1230
if (!toSys) currentSy= sys;
1232
/* copy properties to gistD */
1235
gistD.text= el->text;
1237
return el->el.number;
1240
int GdCells(GpReal px, GpReal py, GpReal qx, GpReal qy,
1241
long width, long height, long nColumns, const GpColor *colors)
1245
long ncells= width*height;
1246
long len= sizeof(GpColor)*ncells;
1247
GeCells *el= currentDr? p_malloc(sizeof(GeCells)) : 0;
1248
if (!el) return MemoryError();
1250
/* make private copy of colors array */
1251
el->rgb = gistA.rgb;
1252
if (gistA.rgb) len *= 3;
1254
el->colors= p_malloc(len);
1255
if (!el->colors) { p_free(el); return MemoryError(); }
1262
if (nColumns==width) {
1263
memcpy(el->colors, colors, len);
1265
GpColor *newcols= el->colors;
1266
long i, rowSize= sizeof(GpColor)*width;
1267
for (i=0 ; i<height ; i++) {
1268
memcpy(newcols, colors, rowSize);
1273
ScanXY(2L, x, y, &linBox);
1274
if (!currentSy) el->el.box= linBox; /* for GeAddElement */
1276
/* set base class members */
1277
GeAddElement(E_CELLS, &el->el);
1279
/* current box not set, mark as unscanned if in system */
1280
GeMarkForScan(&el->el, &linBox);
1282
/* copy properties to gistD */
1287
gistD.width= el->width;
1288
gistD.height= el->height;
1289
gistD.colors= el->colors;
1291
return el->el.number;
1294
int GdFill(long n, const GpColor *colors, const GpReal *px,
1295
const GpReal *py, const long *pn)
1299
if (n<=0) return -1;
1300
el= currentDr? p_malloc(sizeof(GePolys)) : 0;
1301
if (!el) return MemoryError();
1302
el->xlog= el->ylog= 0;
1304
/* make private copy of colors array */
1306
long ncol = (gistA.rgb? 3*n : n);
1307
el->rgb = gistA.rgb;
1308
el->colors= p_malloc(ncol);
1309
if (!el->colors) { p_free(el); return MemoryError(); }
1310
memcpy(el->colors, colors, ncol);
1317
/* make private copy of lengths array */
1318
el->pn= p_malloc(sizeof(long)*n);
1319
if (!el->pn) { p_free(el->colors); p_free(el); return MemoryError(); }
1320
for (ntot=i=0 ; i<n ; i++) {
1325
/* make private copies of x and y arrays */
1326
el->y= Copy2(&el->x, px, py, sizeof(GpReal)*ntot);
1327
if (!el->y) { p_free(el->pn); p_free(el->colors); p_free(el); return -1; }
1330
/* scan for min and max of x and y arrays */
1331
if (n<2 || pn[1]>1) ScanXY(ntot, px, py, &el->linBox);
1332
else ScanXY(ntot-pn[0], px+pn[0], py+pn[0], &el->linBox);
1333
if (!currentSy) el->el.box= el->linBox; /* for GeAddElement */
1335
/* copy relevant attributes from gistA
1336
-- This must be done BEFORE GeAddElement, since the damage
1337
calculation depends on the Margins! */
1338
el->e= gistA.e; /* for edges */
1340
/* set base class members */
1341
GeAddElement(E_POLYS, &el->el);
1343
/* current box not set, mark as unscanned if in system */
1344
GeMarkForScan(&el->el, &el->linBox);
1346
/* copy properties to gistD */
1351
gistD.colors= el->colors;
1353
return el->el.number;
1356
int GdMesh(int noCopy, GaQuadMesh *mesh, int region, int boundary,
1360
GeMesh *el= currentDr? p_malloc(sizeof(GeMesh)) : 0;
1361
if (!el) return MemoryError();
1362
el->el.next= el->el.prev= 0;
1365
len= GeGetMesh(noCopy, mesh, region, el);
1366
if (!len) return -1;
1367
el->boundary= boundary;
1368
el->inhibit= inhibit;
1370
/* copy relevant attributes from gistA
1371
-- This must be done BEFORE GeAddElement, since the damage
1372
calculation depends on the Margins! */
1375
/* set base class members */
1376
GeAddElement(E_MESH, &el->el);
1378
/* current box not set, mark as unscanned if in system */
1379
GeMarkForScan(&el->el, &el->linBox);
1381
/* copy properties to gistD */
1382
gistD.boundary= el->boundary;
1383
gistD.inhibit= el->inhibit;
1385
return el->el.number;
1388
int GdFillMesh(int noCopy, GaQuadMesh *mesh, int region,
1389
GpColor *colors, long nColumns)
1392
GeFill *el= currentDr? p_malloc(sizeof(GeFill)) : 0;
1393
if (!el) return MemoryError();
1394
el->el.next= el->el.prev= 0;
1397
len= GeGetMesh(noCopy, mesh, region, el);
1398
if (!len) return -1;
1400
/* make private copy of colors array */
1401
el->rgb = gistA.rgb;
1402
if (noCopy&NOCOPY_COLORS || !colors) {
1405
long iMax1= mesh->iMax-1;
1406
long len1= len - mesh->jMax - iMax1;
1407
int rgb = gistA.rgb;
1409
el->colors= Copy1(nColumns==iMax1?colors:0, sizeof(GpColor)*len1);
1410
if (!el->colors) { FilledKill(el); return -1; }
1411
if (nColumns!=iMax1) {
1413
for (i=0 ; i<len1 ; i++) {
1415
el->colors[i++]= colors[3*(j+k)];
1416
el->colors[i++]= colors[3*(j+k)+1];
1417
el->colors[i]= colors[3*(j+k)+2];
1419
el->colors[i]= colors[j+k];
1422
if (j==iMax1) { k+= nColumns; j= 0; }
1428
el->nColumns= nColumns;
1430
/* copy relevant attributes from gistA
1431
-- This must be done BEFORE GeAddElement, since the damage
1432
calculation depends on the Margins! */
1433
el->e= gistA.e; /* for edges */
1435
/* set base class members */
1436
GeAddElement(E_FILLED, &el->el);
1438
/* current box not set, mark as unscanned if in system */
1439
GeMarkForScan(&el->el, &el->linBox);
1441
/* copy properties to gistD */
1442
gistD.nColumns= nColumns;
1443
gistD.colors= el->colors;
1445
return el->el.number;
1448
int GdVectors(int noCopy, GaQuadMesh *mesh, int region,
1449
GpReal *u, GpReal *v, GpReal scale)
1452
GeVectors *el= currentDr? p_malloc(sizeof(GeVectors)) : 0;
1453
if (!el) return MemoryError();
1454
el->el.next= el->el.prev= 0;
1457
len= GeGetMesh(noCopy, mesh, region, el);
1458
if (!len) return -1;
1460
/* make private copy of (u,v) arrays */
1461
if (noCopy&NOCOPY_UV) {
1465
el->v= Copy2(&el->u, u, v, sizeof(GpReal)*len);
1466
if (!el->v) { VectorsKill(el); return -1; }
1470
/* copy relevant attributes from gistA
1471
-- This must be done BEFORE GeAddElement, since the damage
1472
calculation depends on the Margins! */
1475
el->vect= gistA.vect;
1477
/* set base class members */
1478
GeAddElement(E_VECTORS, &el->el);
1480
/* current box not set, mark as unscanned if in system */
1481
GeMarkForScan(&el->el, &el->linBox);
1483
/* copy properties to gistD */
1486
gistD.scale= el->scale;
1488
return el->el.number;
1491
int Gd_MakeContours(GeContours *con)
1494
GpReal dphase, *px, *py;
1500
/* Generic properties copied to all contours (m.type reset below) */
1504
marker= gistA.m.type>32? gistA.m.type : 'A';
1505
dphase= 0.25*con->dl.mSpace;
1507
for (i=0 ; i<con->nLevels ; i++) con->groups[i]= 0;
1509
for (i=0 ; i<con->nLevels ; i++) {
1510
gistA.m.type= marker++;
1511
if (marker=='Z'+1 || marker=='z'+1) marker= 'A';
1513
if (GaContourInit(&con->mesh, con->region, con->z, con->levels[i])) {
1514
while (GaContour(&n, &px, &py, &gistA.dl.closed)) {
1515
el= currentDr? p_malloc(sizeof(GeLines)) : 0;
1516
if (!el) return MemoryError();
1518
/* make private copies of x and y arrays */
1519
el->y= Copy2(&el->x, px, py, sizeof(GpReal)*n);
1520
if (!el->y) { p_free(el); return -1; }
1522
el->xlog= el->ylog= 0;
1524
/* scan for min and max of x and y arrays */
1525
ScanXY(n, px, py, &el->linBox);
1526
if (!currentSy) el->el.box= el->linBox; /* for GeAddElement */
1528
el->el.ops= opTables + E_LINES;
1531
el->el.box= el->linBox;
1533
/* GeContour number always matches number of largest element
1534
for GdBeginSy to work properly */
1535
el->el.number= con->el.number= currentDr->nElements++;
1538
prev->el.next= group->el.prev= &el->el;
1539
el->el.prev= &prev->el;
1540
el->el.next= &group->el;
1542
con->groups[i]= group= el;
1543
el->el.next= el->el.prev= &el->el;
1551
gistA.dl.mPhase+= dphase;
1552
if (gistA.dl.mPhase>gistA.dl.mSpace)
1553
gistA.dl.mPhase-= gistA.dl.mSpace;
1561
int GdContours(int noCopy, GaQuadMesh *mesh, int region,
1562
GpReal *z, const GpReal *levels, int nLevels)
1565
GeContours *el= currentDr? p_malloc(sizeof(GeContours)) : 0;
1566
if (!el) return MemoryError();
1567
el->el.next= el->el.prev= 0;
1568
el->z= el->levels= 0;
1572
len= GeGetMesh(noCopy, mesh, region, el);
1573
if (!len) return -1;
1575
/* make private copy of z and levels arrays */
1576
if (noCopy&NOCOPY_Z) {
1579
el->z= Copy1(z, sizeof(GpReal)*len);
1580
if (!el->z) { ContoursKill(el); return -1; }
1583
/* create triangle array now if necessary */
1584
if (noCopy&NOCOPY_MESH && mesh->triangle) {
1585
el->mesh.triangle= mesh->triangle;
1586
el->noCopy|= NOCOPY_TRI;
1587
gistD.noCopy|= NOCOPY_TRI;
1589
el->mesh.triangle= Copy1(mesh->triangle, sizeof(short)*len);
1590
if (!el->mesh.triangle) { ContoursKill(el); return -1; }
1593
/* copy relevant attributes from gistA
1594
-- This must be done BEFORE GeAddElement, since the damage
1595
calculation depends on the Margins! */
1600
/* set base class members */
1601
GeAddElement(E_CONTOURS, &el->el);
1602
if (gistA.m.type==0) el->m.type= AutoMarker(&el->dl, el->el.number);
1604
el->nLevels= nLevels;
1606
el->levels= Copy1(levels, sizeof(GpReal)*nLevels);
1607
if (!el->levels) { ContoursKill(el); return -1; }
1608
el->groups= (GeLines **)p_malloc(sizeof(GeLines *)*nLevels);
1609
if (!el->groups || Gd_MakeContours(el)) { ContoursKill(el); return -1; }
1616
/* current box not set, mark as unscanned if in system */
1617
GeMarkForScan(&el->el, &el->linBox);
1619
/* copy properties to gistD */
1621
gistD.nLevels= el->nLevels;
1622
gistD.levels= el->levels;
1624
return el->el.number;
1627
/* ------------------------------------------------------------------------ */
1629
static void GuessBox(GpBox *box, GpBox *viewport, GaTickStyle *ticks)
1631
GpReal dxmin= 0.0, xmin= viewport->xmin;
1632
GpReal dxmax= 0.0, xmax= viewport->xmax;
1633
GpReal dymin= 0.0, ymin= viewport->ymin;
1634
GpReal dymax= 0.0, ymax= viewport->ymax;
1635
int vf= ticks->vert.flags;
1636
int hf= ticks->horiz.flags;
1637
GpReal vlen= ((((vf&TICK_IN)&&(vf&TICK_OUT))||(vf&TICK_C))? 0.5 : 1.0) *
1638
ticks->vert.tickLen[0];
1639
GpReal hlen= ((((vf&TICK_IN)&&(vf&TICK_OUT))||(vf&TICK_C))? 0.5 : 1.0) *
1640
ticks->horiz.tickLen[0];
1641
GpReal cy= ticks->horiz.textStyle.height;
1642
GpReal cx= ticks->vert.textStyle.height*0.6; /* guess at char width */
1643
GpReal hx= cy*0.6; /* guess at char width */
1646
/* Note-- extra 0.4 for nudged log decades (see DrawXLabels, tick.c) */
1647
cx*= ticks->vert.nDigits+2.4; /* largest width of y label */
1648
hx*= 0.5*(ticks->horiz.nDigits+2.4); /* maximum distance x label
1649
can project past xmin, xmax */
1651
if (((vf&TICK_L)&&(vf&TICK_OUT)) || (vf&TICK_C))
1652
dxmin= ticks->vert.tickOff+vlen;
1653
if (((vf&TICK_U)&&(vf&TICK_OUT)) || (vf&TICK_C))
1654
dxmax= ticks->vert.tickOff+vlen;
1655
if (((hf&TICK_L)&&(hf&TICK_OUT)) || (hf&TICK_C))
1656
dymin= ticks->horiz.tickOff+hlen;
1657
if (((hf&TICK_U)&&(hf&TICK_OUT)) || (hf&TICK_C))
1658
dymax= ticks->horiz.tickOff+hlen;
1660
if (vf & LABEL_L) xmin-= ticks->vert.labelOff+cx;
1661
else if ((hf&(LABEL_L|LABEL_U)) && hx>dxmin) xmin-= hx;
1663
if (vf & LABEL_U) xmax+= ticks->vert.labelOff+cx;
1664
else if ((hf&(LABEL_L|LABEL_U)) && hx>dxmax) xmax+= hx;
1667
if (hf & LABEL_L) ymin-= ticks->horiz.labelOff+2.0*cy;
1668
else if ((vf&(LABEL_L|LABEL_U)) && 0.5*cy>dymin) ymin-= 0.5*cy;
1670
if (hf & LABEL_U) xmax+= ticks->horiz.labelOff+2.0*cy;
1671
else if ((vf&(LABEL_L|LABEL_U)) && 0.5*cy>dymax) ymax+= 0.5*cy;
1674
if (vf & (TICK_L|TICK_U)) {
1675
xmin-= 0.5*ticks->vert.tickStyle.width*DEFAULT_LINE_WIDTH;
1676
xmax+= 0.5*ticks->vert.tickStyle.width*DEFAULT_LINE_WIDTH;
1678
if (hf & (TICK_L|TICK_U)) {
1679
ymin-= 0.5*ticks->horiz.tickStyle.width*DEFAULT_LINE_WIDTH;
1680
ymax+= 0.5*ticks->horiz.tickStyle.width*DEFAULT_LINE_WIDTH;
1688
/* Finally, swallow overflow boxes, assuming 22 characters max */
1689
overflow.xmin= ticks->horiz.xOver;
1690
overflow.ymin= ticks->horiz.yOver-ticks->horiz.textStyle.height*0.2;
1691
overflow.xmax= overflow.xmin+ticks->horiz.textStyle.height*(0.6*22.0);
1692
overflow.ymax= overflow.ymin+ticks->horiz.textStyle.height;
1693
GpSwallow(box, &overflow);
1694
overflow.xmin= ticks->vert.xOver;
1695
overflow.ymin= ticks->vert.yOver-ticks->vert.textStyle.height*0.2;
1696
overflow.xmax= overflow.xmin+ticks->vert.textStyle.height*(0.6*22.0);
1697
overflow.ymax= overflow.ymin+ticks->vert.textStyle.height;
1698
GpSwallow(box, &overflow);
1701
int GdNewSystem(GpBox *viewport, GaTickStyle *ticks)
1706
if (!currentDr) return -1;
1708
/* Adding a new system clears the drawing */
1709
if (currentDr->cleared!=2) ClearDrawing(currentDr);
1710
sysIndex= currentDr->nSystems+1;
1712
sys= p_malloc(sizeof(GeSystem));
1713
if (!sys) return -1;
1714
sys->el.ops= opTables + E_SYSTEM;
1716
sys->el.legend= Copy1(gistD.legend, strlen(gistD.legend)+1);
1717
if (!sys->el.legend) { p_free(sys); return -1; }
1718
} else sys->el.legend= 0;
1719
sys->el.hidden= gistD.hidden;
1722
GdElement *prev= currentDr->systems->el.prev;
1723
prev->next= &sys->el;
1725
sys->el.next= ¤tDr->systems->el;
1726
currentDr->systems->el.prev= &sys->el;
1728
sys->el.prev= sys->el.next= &sys->el;
1729
currentDr->systems= sys;
1732
currentDr->nSystems++;
1736
GuessBox(&sys->el.box, viewport, ticks);
1738
if (viewport->xmin<viewport->xmax) {
1739
sys->trans.viewport.xmin= viewport->xmin;
1740
sys->trans.viewport.xmax= viewport->xmax;
1742
sys->trans.viewport.xmin= viewport->xmax;
1743
sys->trans.viewport.xmax= viewport->xmin;
1745
if (viewport->ymin<viewport->ymax) {
1746
sys->trans.viewport.ymin= viewport->ymin;
1747
sys->trans.viewport.ymax= viewport->ymax;
1749
sys->trans.viewport.ymin= viewport->ymax;
1750
sys->trans.viewport.ymax= viewport->ymin;
1752
sys->trans.window.xmin= sys->trans.window.ymin= 0.0;
1753
sys->trans.window.xmax= sys->trans.window.ymax= 1.0;
1755
sys->flags= D_XMIN | D_XMAX | D_YMIN | D_YMAX;
1757
sys->savedWindow.xmin= sys->savedWindow.ymin= 0.0;
1758
sys->savedWindow.xmax= sys->savedWindow.ymax= 1.0;
1759
sys->savedFlags= D_XMIN | D_XMAX | D_YMIN | D_YMAX;
1761
sys->xtick= sys->ytick= 0;
1762
sys->xlabel= sys->ylabel= 0;
1764
GdSetSystem(sysIndex);
1768
int GdGetSystem(void)
1770
GdElement *sys, *sys0;
1772
if (!currentDr) return -1;
1773
if (!currentDr->systems || !currentSy) return 0;
1775
/* ClearDrawing sets currentSy-- must not be pending */
1776
if (currentDr->cleared==1) ClearDrawing(currentDr);
1778
sys= sys0= (GdElement *)currentDr->systems;
1779
for (sysIndex=1 ; sys!=¤tSy->el ; sysIndex++) {
1781
if (sys==sys0) return -2;
1787
int GdSetSystem(int sysIndex)
1791
if (!currentDr || !currentDr->systems) return E_NONE;
1793
/* ClearDrawing sets currentSy-- must not be pending */
1794
if (currentDr->cleared==1) ClearDrawing(currentDr);
1798
if (sysIndex<1) { /* Set current system to none */
1800
gistD.trans.viewport.xmin= gistD.trans.viewport.xmax=
1801
gistD.trans.viewport.ymin= gistD.trans.viewport.ymax= 0.0;
1806
sys= currentDr->systems;
1808
while (--sysIndex && sys->el.next!=sys0)
1809
sys= (GeSystem *)sys->el.next;
1810
if (sysIndex>0) return E_NONE;
1813
gistD.hidden= sys->el.hidden;
1814
gistD.legend= sys->el.legend;
1815
gistD.ticks= sys->ticks;
1816
gistD.trans.viewport= sys->trans.viewport;
1817
if (GdGetLimits()) {
1824
int GdSetElement(int elIndex)
1826
GdElement *el, *el0;
1827
if (!currentDr) return E_NONE;
1829
el= currentSy? currentSy->elements : currentDr->elements;
1831
if (elIndex<0 || !el) { /* set current element to none */
1838
while (elIndex-- && el->next!=el0) el= el->next;
1839
if (elIndex>=0) return E_NONE;
1843
return el->ops->GetProps(el);
1846
static GdElement *NextConCurve(GdElement *el)
1848
GeContours *con= (GeContours *)currentEl;
1849
GdElement *el0= &con->groups[currentCn]->el;
1851
else if (el->next==el0) el= 0;
1856
int GdSetContour(int levIndex)
1860
if (!currentDr || !currentEl || currentEl->ops->type!=E_CONTOURS)
1862
con= (GeContours *)currentEl;
1863
if (levIndex>=con->nLevels) return E_NONE;
1868
currentCn= levIndex;
1869
el= NextConCurve((GdElement *)0);
1870
if (el) LinesGet(el);
1871
else ContoursGet(con);
1875
static int GeFindIndex(int id, GeSystem *sys)
1878
GdElement *el, *el0;
1879
if (!currentDr) return -1;
1881
el= sys? sys->elements : currentDr->elements;
1886
while (el->number != id) {
1887
if (el->next==el0) return -1;
1895
int GdFindIndex(int id)
1897
return GeFindIndex(id, currentSy);
1900
int GdFindSystem(int id)
1905
if (!currentDr) return -1;
1907
if (GeFindIndex(id, 0)>=0) return 0;
1908
sys= currentDr->systems;
1909
if (!sys) return -1;
1912
while (GeFindIndex(id, sys)<0) {
1913
if (sys->el.next==sys0) return -1;
1914
sys= (GeSystem *)sys->el.next;
1920
int GdAltTick(GaAltTicks *xtick, GaAltLabel *xlabel,
1921
GaAltTicks *ytick, GaAltLabel *ylabel)
1923
if (!currentDr || !currentSy) return 1;
1924
if (xtick) currentSy->xtick= xtick;
1925
if (ytick) currentSy->ytick= ytick;
1926
if (xlabel) currentSy->xlabel= xlabel;
1927
if (ylabel) currentSy->ylabel= ylabel;
1931
/* ------------------------------------------------------------------------ */
1933
int GdEdit(int xyzChanged)
1935
GdElement *el= currentEl;
1936
if (!currentDr || !el) return 1;
1938
/* Changing linestyles or most other simple changes may incur damage
1939
in a way that is very difficult to anticipate, hence, must call
1940
Damage here. On the other hand, the elements need to be rescanned
1941
only if CHANGE_XY or CHANGE_Z has been set, so only set rescan
1942
in this case. If only linestyles and such have been changed, GdScan
1943
will not call Damage again, although if they have it will-- hence, if
1944
you are changing both coordinates and linestyles, there is no way to
1945
avoid "double damage". */
1946
Damage(currentSy, el);
1947
if (currentSy && xyzChanged) currentSy->rescan= 1;
1950
el= NextConCurve((GdElement *)0);
1952
/* legend only changes on first piece of contour */
1953
el->legend= gistD.legend;
1955
/* other line properties propagate to all contour pieces
1956
-- but NEVER attempt to change the (x,y) values */
1957
while ((el= NextConCurve(el))) Gd_LinesSubSet(el);
1961
return el->ops->SetProps(el, xyzChanged);
1966
GdElement *el= currentEl;
1967
if (!currentDr || !el || currentCn>=0) return 1;
1969
/* Damage alert must take place here-- unfortunately, if this remove
1970
changes extreme values, a second call to Damage will be made in
1971
GdScan. Hopefully, GdRemove is a rare enough operation that this
1972
inefficiency is negligible. */
1973
Damage(currentSy, el);
1976
GdElement *prev= el->prev;
1978
currentSy->unscanned= -1;
1979
currentSy->rescan= 0;
1980
currentSy->el.number= -1;
1982
if (el->number==currentSy->unscanned) {
1983
if (el->next != currentSy->elements)
1984
currentSy->unscanned= el->next->number;
1985
else currentSy->unscanned= -1;
1987
if (el->number<currentSy->unscanned && !el->hidden)
1988
currentSy->rescan= 1;
1989
if (el->number==currentSy->el.number)
1990
currentSy->el.number= prev->number;
1994
if (currentSy && el==currentSy->elements) {
1995
if (el->next==el) currentSy->elements= 0;
1996
else currentSy->elements= el->next;
1997
} else if (el==currentDr->elements) {
1998
if (el->next==el) currentDr->elements= 0;
1999
else currentDr->elements= el->next;
2007
int GdGetLimits(void)
2009
if (!currentDr || !currentSy) return 1;
2010
if ((currentSy->rescan || currentSy->unscanned>=0)
2011
&& GdScan(currentSy)) return 1; /* memory manager failure */
2012
gistD.trans.window= currentSy->trans.window;
2013
gistD.flags= currentSy->flags;
2015
if (gistD.flags & D_LOGX) {
2016
gistD.limits.xmin= exp10(gistD.trans.window.xmin);
2017
gistD.limits.xmax= exp10(gistD.trans.window.xmax);
2019
gistD.limits.xmin= gistD.trans.window.xmin;
2020
gistD.limits.xmax= gistD.trans.window.xmax;
2022
if (gistD.flags & D_LOGY) {
2023
gistD.limits.ymin= exp10(gistD.trans.window.ymin);
2024
gistD.limits.ymax= exp10(gistD.trans.window.ymax);
2026
gistD.limits.ymin= gistD.trans.window.ymin;
2027
gistD.limits.ymax= gistD.trans.window.ymax;
2032
int GdSetLimits(void)
2035
if (!currentDr || !currentSy) return 1;
2037
if (gistD.flags & D_LOGX) {
2038
gistD.trans.window.xmin= SAFELOG(gistD.limits.xmin);
2039
gistD.trans.window.xmax= SAFELOG(gistD.limits.xmax);
2041
gistD.trans.window.xmin= gistD.limits.xmin;
2042
gistD.trans.window.xmax= gistD.limits.xmax;
2044
if (gistD.flags & D_LOGY) {
2045
gistD.trans.window.ymin= SAFELOG(gistD.limits.ymin);
2046
gistD.trans.window.ymax= SAFELOG(gistD.limits.ymax);
2048
gistD.trans.window.ymin= gistD.limits.ymin;
2049
gistD.trans.window.ymax= gistD.limits.ymax;
2052
flags= currentSy->flags;
2053
currentSy->flags= gistD.flags;
2055
/* Normally, setting the limits damages the entire system.
2056
However, would like to allow a special case for fixing limits to
2057
their existing extreme values. */
2059
if ( ! ( (flags^gistD.flags) & (~(D_XMIN|D_XMAX|D_YMIN|D_YMAX)) ) ) {
2060
if (((flags&D_XMIN)==(gistD.flags&D_XMIN) || (gistD.flags&D_XMIN)==0) &&
2061
((flags&D_XMAX)==(gistD.flags&D_XMAX) || (gistD.flags&D_XMAX)==0) &&
2062
((flags&D_YMIN)==(gistD.flags&D_YMIN) || (gistD.flags&D_YMIN)==0) &&
2063
((flags&D_YMAX)==(gistD.flags&D_YMAX) || (gistD.flags&D_YMAX)==0)) {
2064
GpBox *w= ¤tSy->trans.window;
2065
if (w->xmin==gistD.trans.window.xmin &&
2066
w->xmax==gistD.trans.window.xmax &&
2067
w->ymin==gistD.trans.window.ymin &&
2068
w->ymax==gistD.trans.window.ymax)
2072
currentSy->trans.window= gistD.trans.window;
2073
currentSy->rescan|= rescan;
2075
/* damage alert takes place in GdScan just before rendering */
2079
int GdSaveLimits(int resetZoomed)
2081
if (!currentDr || !currentSy) return 1;
2082
currentSy->savedWindow= currentSy->trans.window;
2083
currentSy->savedFlags= currentSy->flags;
2084
if (resetZoomed) currentSy->savedFlags&= ~D_ZOOMED;
2088
int GdRevertLimits(int ifZoomed)
2090
if (!currentDr || !currentSy ||
2091
(ifZoomed && !(currentSy->flags&D_ZOOMED))) return 1;
2092
if (currentSy->savedFlags!=currentSy->flags ||
2093
currentSy->savedWindow.xmin!=currentSy->trans.window.xmin ||
2094
currentSy->savedWindow.xmax!=currentSy->trans.window.xmax ||
2095
currentSy->savedWindow.ymin!=currentSy->trans.window.ymin ||
2096
currentSy->savedWindow.ymax!=currentSy->trans.window.ymax) {
2097
currentSy->trans.window= currentSy->savedWindow;
2098
currentSy->flags= currentSy->savedFlags;
2099
currentSy->rescan= 1;
2107
if (!currentDr || !currentSy) return 1;
2109
currentSy->el.hidden= gistD.hidden;
2112
currentSy->el.legend= Copy1(gistD.legend, strlen(gistD.legend)+1);
2116
/* First, damage current coordinate system box. */
2117
Damage(currentSy, (GdElement *)0);
2119
/* Save old box, set new ticks, viewport, and correponding box */
2120
oldBox= currentSy->el.box;
2121
v= ¤tSy->trans.viewport;
2122
currentSy->ticks= gistD.ticks;
2123
*v= gistD.trans.viewport;
2124
GuessBox(¤tSy->el.box, &gistD.trans.viewport, &gistD.ticks);
2126
/* Since stacking order hasn't changed, new box must be damaged
2127
if it is not contained in the old box. */
2128
v= ¤tSy->el.box;
2129
if (v->xmin<oldBox.xmin || v->xmax>oldBox.xmax ||
2130
v->ymin<oldBox.ymin || v->ymax>oldBox.ymax)
2131
Damage(currentSy, (GdElement *)0);
2136
GpBox *GdClearSystem(void)
2140
GeSystem *sys, *sys0;
2141
GdElement *el, *el0;
2142
/* Intended for use with animation... */
2143
if (!currentDr || !currentSy) return 0;
2145
Gd_KillRing(currentSy->elements);
2146
currentSy->elements= 0;
2147
currentSy->el.number= currentSy->unscanned= -1;
2148
currentSy->rescan= 0;
2150
sys0= currentDr->systems;
2151
el0= currentDr->elements;
2153
if ((sys= sys0)) do {
2154
if (sys==currentSy) continue;
2155
n= currentSy->el.number;
2157
sys= (GeSystem *)sys->el.next;
2158
} while (sys!=sys0);
2164
currentDr->nElements= nel+1;
2166
if (currentSy->flags & (D_XMIN|D_XMAX|D_YMIN|D_YMAX)) {
2167
/* Some extreme value set, damage whole box */
2168
dBox= ¤tSy->el.box;
2169
Damage(currentSy, (GdElement *)0);
2171
/* All limits fixed, damage only viewport */
2172
dBox= ¤tSy->trans.viewport;
2173
Damage(currentSy, ¤tSy->el);
2179
/* ------------------------------------------------------------------------ */