1 /*
2  * $Id: engine.c,v 1.1 2005-09-18 22:04:19 dhmunro Exp $
3  * Implement common properties of all GIST engines
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 "gist.h"
12 #include "engine.h"
13 #include "draw.h"
14 #include "pstdlib.h"
15 
16 Engine *gistEngines= 0;
17 Engine *gistActive= 0;
18 Engine *gistPreempt= 0;
19 
20 #include <string.h>
21 
22 static void DefaultClearArea(Engine *engine, GpBox *box);
23 static void MoreScratch(long np, long ns);
24 
25 /* ------------------------------------------------------------------------ */
26 
27 /* ARGSUSED */
DefaultClearArea(Engine * engine,GpBox * box)28 static void DefaultClearArea(Engine *engine, GpBox *box)
29 {
30   /* Default ClearArea triggers complete redraw */
31   engine->Clear(engine, CONDITIONALLY);
32   engine->lastDrawn= -1;
33   engine->systemsSeen[0]= engine->systemsSeen[1]= 0;
34   engine->damaged= engine->inhibit= 0;
35 }
36 
GpNewEngine(long size,char * name,g_callbacks * on,GpTransform * transform,int landscape,void (* Kill)(Engine *),int (* Clear)(Engine *,int),int (* Flush)(Engine *),void (* ChangeMap)(Engine *),int (* ChangePalette)(Engine *),int (* DrawLines)(Engine *,long,const GpReal *,const GpReal *,int,int),int (* DrawMarkers)(Engine *,long,const GpReal *,const GpReal *),int (* DrwText)(Engine * e,GpReal,GpReal,const char *),int (* DrawFill)(Engine *,long,const GpReal *,const GpReal *),int (* DrawCells)(Engine *,GpReal,GpReal,GpReal,GpReal,long,long,long,const GpColor *),int (* DrawDisjoint)(Engine *,long,const GpReal *,const GpReal *,const GpReal *,const GpReal *))37 Engine *GpNewEngine(long size, char *name, g_callbacks *on,
38                     GpTransform *transform, int landscape,
39   void (*Kill)(Engine*), int (*Clear)(Engine*,int), int (*Flush)(Engine*),
40   void (*ChangeMap)(Engine*), int (*ChangePalette)(Engine*),
41   int (*DrawLines)(Engine*,long,const GpReal*,const GpReal*,int,int),
42   int (*DrawMarkers)(Engine*,long,const GpReal*,const GpReal *),
43   int (*DrwText)(Engine*e,GpReal,GpReal,const char*),
44   int (*DrawFill)(Engine*,long,const GpReal*,const GpReal*),
45   int (*DrawCells)(Engine*,GpReal,GpReal,GpReal,GpReal,
46                    long,long,long,const GpColor*),
47   int (*DrawDisjoint)(Engine*,long,const GpReal*,const GpReal*,
48                       const GpReal*,const GpReal*))
49 {
50   long lname= name? strlen(name) : 0;
51   Engine *engine;
52   /* For Electric Fence package and maybe others, it is nice to ensure
53      that size of block allocated for Engine is a multiple of the size
54      of the most restrictively aligned object which can be in any
55      Engine; assume this is a double.  */
56   lname= (lname/sizeof(double) + 1)*sizeof(double);  /* >= lname+1 */
57   engine= (Engine *)p_malloc(size+lname);
58   if (!engine) return 0;
59 
60   /* Fill in Engine properties, link into gistEngines list */
61   engine->next= gistEngines;
62   gistEngines= engine;
63   engine->nextActive= 0;
64   engine->name= (char *)engine + size;
65   strcpy(name? engine->name : "", name);
66   engine->on = on;
67   engine->active= 0;
68   engine->marked= 0;
69 
70   engine->transform= *transform;
71   engine->landscape= landscape? 1 : 0;
72   GpDeviceMap(engine);
73   /* (a proper map will be installed when the engine is activated) */
74   engine->map.x.scale= engine->map.y.scale= 1.0;
75   engine->map.x.offset= engine->map.y.offset= 0.0;
76 
77   /* No pseudocolor map initially */
78   engine->colorChange= 0;
79   engine->colorMode= 0;
80   engine->nColors= 0;
81   engine->palette= 0;
82 
83   /* No associated drawing initially */
84   engine->drawing= 0;
85   engine->lastDrawn= -1;
86   engine->systemsSeen[0]= engine->systemsSeen[1]= 0;
87   engine->inhibit= 0;
88   engine->damaged= 0;  /* causes Clear if no ClearArea virtual function */
89   engine->damage.xmin= engine->damage.xmax=
90     engine->damage.ymin= engine->damage.ymax= 0.0;
91 
92   /* Fill in virtual function table */
93   engine->Kill= Kill;
94   engine->Clear= Clear;
95   engine->Flush= Flush;
96   engine->ChangeMap= ChangeMap;
97   engine->ChangePalette= ChangePalette;
98   engine->DrawLines= DrawLines;
99   engine->DrawMarkers= DrawMarkers;
100   engine->DrwText= DrwText;
101   engine->DrawFill= DrawFill;
102   engine->DrawCells= DrawCells;
103   engine->DrawDisjoint= DrawDisjoint;
104   engine->ClearArea= &DefaultClearArea;   /* damage causes complete redraw */
105 
106   return engine;
107 }
108 
GpDelEngine(Engine * engine)109 void GpDelEngine(Engine *engine)
110 {
111   Engine *eng= gistEngines;
112   if (!engine) return;
113 
114   /* Unlink from gistEngines list */
115   if (engine->active) GpDeactivate(engine);
116   if (eng==engine) gistEngines= engine->next;
117   else {
118     /* Because of recursive deletes necessary to deal with X window
119        deletions (see xbasic.c:ShutDown, hlevel.c:ShutDownDev), if
120        the engine has already been removed from the list, it means that
121        this routine is being called for the second time for this engine,
122        and p_free must NOT be called.  Fix this someday.  */
123     while (eng && eng->next!=engine) eng= eng->next;
124     if (!eng) return;
125     eng->next= engine->next;
126   }
127 
128   p_free(engine);
129 }
130 
131 /* ------------------------------------------------------------------------ */
132 
GpKillEngine(Engine * engine)133 void GpKillEngine(Engine *engine)
134 {
135   if (engine) engine->Kill(engine);
136 }
137 
GpActivate(Engine * engine)138 int GpActivate(Engine *engine)
139 {
140   if (!engine) return 1;
141   if (!engine->active) {
142     engine->active= 1;
143     engine->nextActive= gistActive;
144     gistActive= engine;
145     engine->ChangeMap(engine);
146   }
147   return 0;
148 }
149 
GpDeactivate(Engine * engine)150 int GpDeactivate(Engine *engine)
151 {
152   if (!engine) return 1;
153   if (engine->active) {
154     Engine *active= gistActive;
155     engine->active= 0;
156     if (active==engine) gistActive= engine->nextActive;
157     else {
158       while (active->nextActive!=engine) active= active->nextActive;
159       active->nextActive= engine->nextActive;
160     }
161   }
162   return 0;
163 }
164 
GpPreempt(Engine * engine)165 int GpPreempt(Engine *engine)
166 {
167   gistPreempt= engine;
168   if (engine && !engine->active) engine->ChangeMap(engine);
169   return 0;
170 }
171 
GpActive(Engine * engine)172 int GpActive(Engine *engine)
173 {
174   if (!engine) return 0;
175   return engine==gistPreempt? 1 : engine->active;
176 }
177 
GpClear(Engine * engine,int flag)178 int GpClear(Engine *engine, int flag)
179 {
180   int value= 0;
181   if (!engine) {
182     for (engine=GpNextActive(0) ; engine ; engine=GpNextActive(engine)) {
183       engine->damaged= engine->inhibit= 0;
184       engine->lastDrawn= -1;
185       engine->systemsSeen[0]= engine->systemsSeen[1]= 0;
186       value|= engine->Clear(engine, flag);
187     }
188   } else {
189     engine->damaged= engine->inhibit= 0;
190     engine->lastDrawn= -1;
191     engine->systemsSeen[0]= engine->systemsSeen[1]= 0;
192     value= engine->Clear(engine, flag);
193   }
194   return value;
195 }
196 
GpFlush(Engine * engine)197 int GpFlush(Engine *engine)
198 {
199   if (!engine) {
200     int value= 0;
201     for (engine=GpNextActive(0) ; engine ; engine=GpNextActive(engine))
202       value|= engine->Flush(engine);
203     return value;
204   }
205   return engine->Flush(engine);
206 }
207 
GpNextEngine(Engine * engine)208 Engine *GpNextEngine(Engine *engine)
209 {
210   return engine? engine->next : gistEngines;
211 }
212 
GpNextActive(Engine * engine)213 Engine *GpNextActive(Engine *engine)
214 {
215   if (gistPreempt) return engine? 0 : gistPreempt;
216   else return engine? engine->nextActive : gistActive;
217 }
218 
219 /* ------------------------------------------------------------------------ */
220 
GpSetTrans(const GpTransform * trans)221 int GpSetTrans(const GpTransform *trans)
222 {
223   Engine *engine;
224 
225   if (trans!=&gistT) gistT= *trans;
226 
227   for (engine=GpNextActive(0) ; engine ; engine=GpNextActive(engine))
228     engine->ChangeMap(engine);
229 
230   return 0;
231 }
232 
GpLandscape(Engine * engine,int landscape)233 int GpLandscape(Engine *engine, int landscape)
234 {
235   if (!engine) {
236     for (engine=GpNextActive(0) ; engine ; engine=GpNextActive(engine))
237       engine->landscape= landscape;
238   } else {
239     engine->landscape= landscape;
240   }
241   return 0;
242 }
243 
GpSetMap(const GpBox * src,const GpBox * dst,GpXYMap * map)244 void GpSetMap(const GpBox *src, const GpBox *dst, GpXYMap *map)
245 {
246   map->x.scale= (dst->xmax-dst->xmin)/(src->xmax-src->xmin);
247   map->x.offset=  dst->xmin - map->x.scale*src->xmin;
248   map->y.scale= (dst->ymax-dst->ymin)/(src->ymax-src->ymin);
249   map->y.offset=  dst->ymin - map->y.scale*src->ymin;
250 }
251 
GpDeviceMap(Engine * engine)252 void GpDeviceMap(Engine *engine)
253 {
254   GpSetMap(&engine->transform.viewport, &engine->transform.window,
255            &engine->devMap);
256 }
257 
GpComposeMap(Engine * engine)258 void GpComposeMap(Engine *engine)
259 {
260   GpMap *devx= &engine->devMap.x;
261   GpMap *devy= &engine->devMap.y;
262   GpMap *mapx= &engine->map.x;
263   GpMap *mapy= &engine->map.y;
264   mapx->scale=
265     devx->scale*(gistT.viewport.xmax-gistT.viewport.xmin)/
266                 (gistT.window.xmax-gistT.window.xmin);
267   mapx->offset= devx->offset + devx->scale*gistT.viewport.xmin -
268                 mapx->scale*gistT.window.xmin;
269   mapy->scale=
270     devy->scale*(gistT.viewport.ymax-gistT.viewport.ymin)/
271                 (gistT.window.ymax-gistT.window.ymin);
272   mapy->offset= devy->offset + devy->scale*gistT.viewport.ymin -
273                 mapy->scale*gistT.window.ymin;
274 }
275 
276 /* ------------------------------------------------------------------------ */
277 
278 /* Scratch space used by GpIntPoints and GpIntSegs */
279 static void *scratch= 0;
280 static long scratchPoints= 0, scratchSegs= 0;
281 
MoreScratch(long np,long ns)282 static void MoreScratch(long np, long ns)
283 {
284   if (scratch) p_free(scratch);
285   if (np) {
286     np+= 64;
287     scratch= (void *)p_malloc(sizeof(GpPoint)*np);
288     scratchPoints= np;
289     scratchSegs= (sizeof(GpPoint)*np)/sizeof(GpSegment);
290   } else {
291     ns+= 32;
292     scratch= (void *)p_malloc(sizeof(GpSegment)*ns);
293     scratchSegs= ns;
294     scratchPoints= (sizeof(GpSegment)*ns)/sizeof(GpPoint);
295   }
296 }
297 
GpIntPoints(const GpXYMap * map,long maxPoints,long n,const GpReal * x,const GpReal * y,GpPoint ** result)298 long GpIntPoints(const GpXYMap *map, long maxPoints, long n,
299                  const GpReal *x, const GpReal *y, GpPoint **result)
300 {
301   GpReal scalx= map->x.scale, offx= map->x.offset;
302   GpReal scaly= map->y.scale, offy= map->y.offset;
303   long i, np= maxPoints<n? maxPoints : n;
304   GpPoint *point;
305 
306   if (np+1>scratchPoints) MoreScratch(np+1, 0); /* allow for closure pt */
307   *result= point= scratch;
308 
309   for (i=0 ; i<np ; i++) {
310     point[i].x= (short)(scalx*x[i]+offx);
311     point[i].y= (short)(scaly*y[i]+offy);
312   }
313 
314   return np;
315 }
316 
GpIntSegs(const GpXYMap * map,long maxSegs,long n,const GpReal * x1,const GpReal * y1,const GpReal * x2,const GpReal * y2,GpSegment ** result)317 long GpIntSegs(const GpXYMap *map, long maxSegs, long n,
318                const GpReal *x1, const GpReal *y1,
319                const GpReal *x2, const GpReal *y2, GpSegment **result)
320 {
321   GpReal scalx= map->x.scale, offx= map->x.offset;
322   GpReal scaly= map->y.scale, offy= map->y.offset;
323   long i, ns= maxSegs<n? maxSegs : n;
324   GpSegment *seg;
325 
326   if (ns>scratchSegs) MoreScratch(0, ns);
327   *result= seg= scratch;
328 
329   for (i=0 ; i<ns ; i++) {
330     seg[i].x1= (short)(scalx*x1[i]+offx);
331     seg[i].y1= (short)(scaly*y1[i]+offy);
332     seg[i].x2= (short)(scalx*x2[i]+offx);
333     seg[i].y2= (short)(scaly*y2[i]+offy);
334   }
335 
336   return ns;
337 }
338 
339 /* ------------------------------------------------------------------------ */
340 
GpPutGray(int nColors,GpColorCell * palette)341 void GpPutGray(int nColors, GpColorCell *palette)
342 {
343   /*
344   while (nColors--) {
345     palette->gray=
346       ((int)palette->red+(int)palette->green+(int)palette->blue)/3;
347     palette++;
348   }
349   */
350 }
351 
GpPutNTSC(int nColors,GpColorCell * palette)352 void GpPutNTSC(int nColors, GpColorCell *palette)
353 {
354   /*
355   while (nColors--) {
356     palette->gray=
357       (30*(int)palette->red+59*(int)palette->green+11*(int)palette->blue)/100;
358     palette++;
359   }
360   */
361 }
362 
GpPutRGB(int nColors,GpColorCell * palette)363 void GpPutRGB(int nColors, GpColorCell *palette)
364 {
365   /*
366   while (nColors--) {
367     palette->red= palette->green= palette->blue= palette->gray;
368     palette++;
369   }
370   */
371 }
372 
GpSetPalette(Engine * engine,GpColorCell * palette,int nColors)373 int GpSetPalette(Engine *engine, GpColorCell *palette, int nColors)
374 {
375   if (!engine) return 0;
376   if (nColors<0) {
377     palette= 0;
378     nColors= 0;
379   }
380   engine->palette= palette;
381   engine->nColors= nColors;
382   engine->colorChange= 1;
383   return engine->ChangePalette(engine);
384 }
385 
GpGetPalette(Engine * engine,GpColorCell ** palette)386 int GpGetPalette(Engine *engine, GpColorCell **palette)
387 {
388   *palette= engine? engine->palette : 0;
389   return engine? engine->nColors : 0;
390 }
391 
GpDumpColors(Engine * engine,int colorMode)392 int GpDumpColors(Engine *engine, int colorMode)
393 {
394   if (!engine) {
395     for (engine=GpNextActive(0) ; engine ; engine=GpNextActive(engine))
396       { engine->colorMode= colorMode;   engine->colorChange= 1; }
397   } else {
398     engine->colorMode= colorMode;   engine->colorChange= 1;
399   }
400   return 0;
401 }
402 
403 /* ------------------------------------------------------------------------ */
404 
GpClipCells(GpMap * map,GpReal * px,GpReal * qx,GpReal xmin,GpReal xmax,long ncells,long * off)405 long GpClipCells(GpMap *map, GpReal *px, GpReal *qx,
406                  GpReal xmin, GpReal xmax, long ncells, long *off)
407 {
408   long imin, imax;
409   GpReal p, q, dx;
410   GpReal scale= map->scale;
411   GpReal offset= map->offset;
412 
413   xmin= xmin*scale+offset;
414   xmax= xmax*scale+offset;
415   if (xmin>xmax) {GpReal tmp=xmin; xmin=xmax; xmax=tmp;}
416   p= (*px)*scale+offset;
417   q= (*qx)*scale+offset;
418 
419   if (p<q && q>=xmin && p<=xmax) {
420     dx= (q-p)/(GpReal)ncells;
421     if (p<xmin) {
422       imin= (long)((xmin-p)/dx);
423       p+= dx*(GpReal)imin;
424     } else {
425       imin= 0;
426     }
427     if (q>xmax) {
428       imax= (long)((q-xmax)/dx);
429       q-= dx*(GpReal)imax;
430       imax= ncells-imax;
431     } else {
432       imax= ncells;
433     }
434     if (imax-imin<2) {
435       if (imax==imin) {
436         if (p<xmin) p= xmin;
437         if (q>xmax) q= xmax;
438       } else {
439         if (p<xmin && q>xmax) {
440           if (q-xmax > xmin-p) { q-= xmin-p;  p= xmin; }
441           else { p+= q-xmax;  q= xmax; }
442         }
443       }
444     }
445   } else if (p>q && p>=xmin && q<=xmax) {
446     dx= (p-q)/(GpReal)ncells;
447     if (q<xmin) {
448       imax= (long)((xmin-q)/dx);
449       q+= dx*(GpReal)imax;
450       imax= ncells-imax;
451     } else {
452       imax= ncells;
453     }
454     if (p>xmax) {
455       imin= (long)((p-xmax)/dx);
456       p-= dx*(GpReal)imin;
457     } else {
458       imin= 0;
459     }
460     if (imax-imin<2) {
461       if (imax==imin) {
462         if (q<xmin) q= xmin;
463         if (p>xmax) p= xmax;
464       } else {
465         if (q<xmin && p>xmax) {
466           if (p-xmax > xmin-q) { p-= xmin-q;  q= xmin; }
467           else { q+= p-xmax;  p= xmax; }
468         }
469       }
470     }
471   } else {
472     imin= 0;
473     imax= -1;
474   }
475 
476   *px= p;
477   *qx= q;
478   *off= imin;
479 
480   return imax-imin;
481 }
482 
483 /* ------------------------------------------------------------------------ */
484 
GpIntersect(const GpBox * box1,const GpBox * box2)485 int GpIntersect(const GpBox *box1, const GpBox *box2)
486 {
487   /* Algorithm assumes min<max for x and y in both boxes */
488   return box1->xmin<=box2->xmax && box1->xmax>=box2->xmin &&
489          box1->ymin<=box2->ymax && box1->ymax>=box2->ymin;
490 }
491 
GpContains(const GpBox * box1,const GpBox * box2)492 int GpContains(const GpBox *box1, const GpBox *box2)
493 {
494   /* Algorithm assumes min<max for x and y in both boxes */
495   return box1->xmin<=box2->xmin && box1->xmax>=box2->xmax &&
496          box1->ymin<=box2->ymin && box1->ymax>=box2->ymax;
497 }
498 
GpSwallow(GpBox * preditor,const GpBox * prey)499 void GpSwallow(GpBox *preditor, const GpBox *prey)
500 {
501   /* Algorithm assumes min<max for x and y in both boxes */
502   if (preditor->xmin>prey->xmin) preditor->xmin= prey->xmin;
503   if (preditor->xmax<prey->xmax) preditor->xmax= prey->xmax;
504   if (preditor->ymin>prey->ymin) preditor->ymin= prey->ymin;
505   if (preditor->ymax<prey->ymax) preditor->ymax= prey->ymax;
506 }
507 
508 /* ------------------------------------------------------------------------ */
509 
510 /* These recondite routines are required to handle editing a drawing
511    on one or more interactive engines.  The restriction to few
512    routines builds in certain inefficiencies; if every drawing were always
513    associated with one interactive engine some of the inefficiency could
514    be reduced.  These are not intended for external use.  */
515 
516 extern int gdNowRendering, gdMaxRendered;
517 int gdNowRendering= -1;
518 int gdMaxRendered= -1;
519 
GdBeginDr(Drauing * drawing,GpBox * damage,int landscape)520 int GdBeginDr(Drauing *drawing, GpBox *damage, int landscape)
521 {
522   int needToRedraw= 0;
523   Engine *eng;
524 
525   if (damage) {
526     /* If drawing has incurred damage, report damage to ALL engines
527        interested in the drawing (not just active engines).  */
528     for (eng=GpNextEngine(0) ; eng ; eng=GpNextEngine(eng))
529       if (eng->drawing==drawing) GpDamage(eng, drawing, damage);
530   }
531 
532   /* Loop on active engines to alert them that drawing is coming.  */
533   for (eng=GpNextActive(0) ; eng ; eng=GpNextActive(eng)) {
534     if (eng->drawing!=drawing) {
535       /* This engine is not marked as interested in this drawing.
536          Mark it, and reset damaged and lastDrawn flags so that no
537          elements will be inhibited.  */
538       eng->drawing= drawing;
539       eng->lastDrawn= -1;
540       eng->damaged= 0;
541       if (landscape != eng->landscape) {
542         eng->landscape= landscape;
543         /* This change will be detected and acted upon by the first call
544            to the ChangeMap method (GpSetTrans).  */
545       }
546       /* The semantics here are subtle --
547          After a ClearDrawing, GdDetach zeroes eng->drawing in order to
548          communicate that the drawing has been cleared.  Thus, the code
549          gets here on a GdDraw after the drawing has been cleared, so
550          the time has come to carry out the deferred clearing of this
551          engine's plotting surface.  */
552       GpClear(eng, CONDITIONALLY);
553       needToRedraw= 1;
554 
555     } else if (eng->damaged) {
556       /* This engine was interested in the drawing, which has been
557          damaged.  Clear damaged area in preparation for repair work.
558          (This is redundant if the damage was due to an X windows
559           expose event, but the resulting inefficiency is very small.)  */
560       eng->ClearArea(eng, &eng->damage);
561       needToRedraw= 1;
562 
563     } else if (eng->lastDrawn<drawing->nElements-1) {
564       needToRedraw= 1;
565     }
566   }
567 
568   gdNowRendering= gdMaxRendered= -1;
569   return needToRedraw;
570 }
571 
GdBeginSy(GpBox * tickOut,GpBox * tickIn,GpBox * viewport,int number,int sysIndex)572 int GdBeginSy(GpBox *tickOut, GpBox *tickIn, GpBox *viewport,
573               int number, int sysIndex)
574 {
575   Engine *eng;
576   int value= 0;
577   long sysMask;
578 
579   /* Note that this is harmless if sysIndex>2*sizeof(long)--
580      just slightly inefficient in that ticks and elements will ALWAYS
581      be drawn...  This shouldn't be a practical problem.  */
582   if (sysIndex>sizeof(long)) {
583     sysMask= 1 << (sysIndex-sizeof(long));
584     sysIndex= 1;
585   } else {
586     sysMask= 1 << sysIndex;
587     sysIndex= 0;
588   }
589 
590   /* Loop on active engines to determine whether any require ticks or
591      elements to be drawn.  Set inhibit switches for ticks.  */
592   for (eng=GpNextActive(0) ; eng ; eng=GpNextActive(eng)) {
593     if ( ! (eng->systemsSeen[sysIndex] & sysMask) ) {
594       /* this engine has never seen this system */
595       value|= 3;
596       eng->inhibit= 0;
597       eng->systemsSeen[sysIndex]|= sysMask;
598 
599     } else if (eng->damaged && GpIntersect(tickOut, &eng->damage)) {
600       /* engine damage touches this coordinate system--
601          redraw ticks if region between tickIn and tickOut damaged,
602          redraw elements if viewport damaged */
603       if (!tickIn || !GpContains(tickIn, &eng->damage)) {
604         value|= 2;
605         eng->inhibit= 0;
606       } else eng->inhibit= 1;
607       if (number>eng->lastDrawn || GpIntersect(viewport, &eng->damage))
608         value|= 1;
609 
610     } else {
611       /* engine undamaged or damage doesn't touch this system--
612          redraw elements if any new ones, don't redraw ticks */
613       eng->inhibit= 1;
614       if (number>eng->lastDrawn) value|= 1;
615     }
616   }
617 
618   return value;
619 }
620 
GdBeginEl(GpBox * box,int number)621 int GdBeginEl(GpBox *box, int number)
622 {
623   Engine *eng;
624   int value= 0;
625 
626   /* Loop on active engines to determine whether any require this
627      element to be drawn, and to set inhibit switches so that some
628      may draw it and others not.  */
629   for (eng=GpNextActive(0) ; eng ; eng=GpNextActive(eng)) {
630     if (number>eng->lastDrawn) {
631       /* this engine hasn't seen this element before */
632       eng->inhibit= 0;
633       value= 1;
634       if (eng->damaged && gdMaxRendered<=eng->lastDrawn) {
635         /* If this is the first new element, the damage flag
636            must be reset, and ChangeMap must be called to set the
637            clip rectangle back to its undamaged boundary.  */
638         eng->damaged= 0;
639         eng->ChangeMap(eng);
640       }
641 
642     } else if (box && eng->damaged && GpIntersect(box, &eng->damage)) {
643       /* engine damage touches this element */
644       eng->inhibit= 0;
645       value= 1;
646 
647     } else {
648       /* this element has been seen before and hasn't been damaged */
649       eng->inhibit= 1;
650     }
651 
652     /* set number of element currently being drawn for GdEndDr */
653     gdNowRendering= number;
654     if (gdMaxRendered<gdNowRendering) gdMaxRendered= gdNowRendering;
655   }
656 
657   return value;
658 }
659 
GdEndDr(void)660 void GdEndDr(void)
661 {
662   Engine *eng;
663   /* Done with this drawing- reset inhibit, damaged, and lastDrawn flags */
664   for (eng=GpNextActive(0) ; eng ; eng=GpNextActive(eng)) {
665     if (eng->lastDrawn<gdMaxRendered) eng->lastDrawn= gdMaxRendered;
666     eng->inhibit= eng->damaged= 0;
667   }
668 }
669 
GpDamage(Engine * eng,Drauing * drawing,GpBox * box)670 void GpDamage(Engine *eng, Drauing *drawing, GpBox *box)
671 {
672   if (eng->drawing!=drawing || !eng->marked) return;
673   if (eng->ClearArea==&DefaultClearArea) {
674     /* This engine doesn't need to record the damage box */
675     eng->damaged= 1;
676   } else if (eng->damaged) {
677     /* drawing is already damaged on this engine */
678     if (eng->damage.xmin>box->xmin) eng->damage.xmin= box->xmin;
679     if (eng->damage.xmax<box->xmax) eng->damage.xmax= box->xmax;
680     if (eng->damage.ymin>box->ymin) eng->damage.ymin= box->ymin;
681     if (eng->damage.ymax<box->ymax) eng->damage.ymax= box->ymax;
682   } else {
683     /* drawing is currently undamaged on this engine */
684     eng->damaged= 1;
685     eng->damage= *box;
686   }
687 }
688 
GdDetach(Drauing * drawing,Engine * engine)689 void GdDetach(Drauing *drawing, Engine *engine)
690 {
691   Engine *eng;
692   for (eng=GpNextEngine(0) ; eng ; eng=GpNextEngine(eng)) {
693     if (!drawing || eng->drawing==drawing) {
694       eng->drawing= 0;
695       eng->inhibit= eng->damaged= 0;
696       eng->lastDrawn= -1;
697     }
698   }
699 }
700 
701 /* ------------------------------------------------------------------------ */
702