4
* $Id: engine.c,v 1.1 2003/03/08 15:26:45 travo Exp $
6
* Implement common properties of all GIST engines
9
/* Copyright (c) 1994. The Regents of the University of California.
10
All rights reserved. */
17
Engine *gistEngines= 0;
18
Engine *gistActive= 0;
19
Engine *gistPreempt= 0;
23
static void DefaultClearArea(Engine *engine, GpBox *box);
24
static void MoreScratch(long np, long ns);
26
/* ------------------------------------------------------------------------ */
29
static void DefaultClearArea(Engine *engine, GpBox *box)
31
/* Default ClearArea triggers complete redraw */
32
engine->Clear(engine, CONDITIONALLY);
33
engine->lastDrawn= -1;
34
engine->systemsSeen[0]= engine->systemsSeen[1]= 0;
35
engine->damaged= engine->inhibit= 0;
38
Engine *GpNewEngine(long size, char *name, char *type,
39
GpTransform *transform, int landscape,
40
void (*Kill)(Engine*), int (*Clear)(Engine*,int), int (*Flush)(Engine*),
41
void (*ChangeMap)(Engine*), int (*ChangePalette)(Engine*),
42
int (*DrawLines)(Engine*,long,const GpReal*,const GpReal*,int,int),
43
int (*DrawMarkers)(Engine*,long,const GpReal*,const GpReal *),
44
int (*DrwText)(Engine*e,GpReal,GpReal,const char*),
45
int (*DrawFill)(Engine*,long,const GpReal*,const GpReal*),
46
int (*DrawCells)(Engine*,GpReal,GpReal,GpReal,GpReal,
47
long,long,long,const GpColor*),
48
int (*DrawDisjoint)(Engine*,long,const GpReal*,const GpReal*,
49
const GpReal*,const GpReal*))
51
long lname= name? strlen(name) : 0;
53
/* For Electric Fence package and maybe others, it is nice to ensure
54
that size of block allocated for Engine is a multiple of the size
55
of the most restrictively aligned object which can be in any
56
Engine; assume this is a double. */
57
lname= (lname/sizeof(double) + 1)*sizeof(double); /* >= lname+1 */
58
engine= (Engine *)p_malloc(size+lname);
59
if (!engine) return 0;
61
/* Fill in Engine properties, link into gistEngines list */
62
engine->next= gistEngines;
64
engine->nextActive= 0;
65
engine->name= (char *)engine + size;
66
strcpy(name? engine->name : "", name);
71
engine->transform= *transform;
72
engine->landscape= landscape? 1 : 0;
74
/* (a proper map will be installed when the engine is activated) */
75
engine->map.x.scale= engine->map.y.scale= 1.0;
76
engine->map.x.offset= engine->map.y.offset= 0.0;
78
/* No pseudocolor map initially */
79
engine->colorChange= 0;
84
/* No associated drawing initially */
86
engine->lastDrawn= -1;
87
engine->systemsSeen[0]= engine->systemsSeen[1]= 0;
89
engine->damaged= 0; /* causes Clear if no ClearArea virtual function */
90
engine->damage.xmin= engine->damage.xmax=
91
engine->damage.ymin= engine->damage.ymax= 0.0;
93
/* Fill in virtual function table */
97
engine->ChangeMap= ChangeMap;
98
engine->ChangePalette= ChangePalette;
99
engine->DrawLines= DrawLines;
100
engine->DrawMarkers= DrawMarkers;
101
engine->DrwText= DrwText;
102
engine->DrawFill= DrawFill;
103
engine->DrawCells= DrawCells;
104
engine->DrawDisjoint= DrawDisjoint;
105
engine->ClearArea= &DefaultClearArea; /* damage causes complete redraw */
110
void GpDelEngine(Engine *engine)
112
Engine *eng= gistEngines;
115
/* Unlink from gistEngines list */
116
if (engine->active) GpDeactivate(engine);
117
if (eng==engine) gistEngines= engine->next;
119
/* Because of recursive deletes necessary to deal with X window
120
deletions (see xbasic.c:ShutDown, hlevel.c:ShutDownDev), if
121
the engine has already been removed from the list, it means that
122
this routine is being called for the second time for this engine,
123
and p_free must NOT be called. Fix this someday. */
124
while (eng && eng->next!=engine) eng= eng->next;
126
eng->next= engine->next;
132
/* ------------------------------------------------------------------------ */
134
void GpKillEngine(Engine *engine)
136
if (engine) engine->Kill(engine);
139
int GpActivate(Engine *engine)
141
if (!engine) return 1;
142
if (!engine->active) {
144
engine->nextActive= gistActive;
146
engine->ChangeMap(engine);
151
int GpDeactivate(Engine *engine)
153
if (!engine) return 1;
154
if (engine->active) {
155
Engine *active= gistActive;
157
if (active==engine) gistActive= engine->nextActive;
159
while (active->nextActive!=engine) active= active->nextActive;
160
active->nextActive= engine->nextActive;
166
int GpPreempt(Engine *engine)
169
if (engine && !engine->active) engine->ChangeMap(engine);
173
int GpActive(Engine *engine)
175
if (!engine) return 0;
176
return engine==gistPreempt? 1 : engine->active;
179
int GpClear(Engine *engine, int flag)
183
for (engine=GpNextActive(0) ; engine ; engine=GpNextActive(engine)) {
184
engine->damaged= engine->inhibit= 0;
185
engine->lastDrawn= -1;
186
engine->systemsSeen[0]= engine->systemsSeen[1]= 0;
187
value|= engine->Clear(engine, flag);
190
engine->damaged= engine->inhibit= 0;
191
engine->lastDrawn= -1;
192
engine->systemsSeen[0]= engine->systemsSeen[1]= 0;
193
value= engine->Clear(engine, flag);
198
int GpFlush(Engine *engine)
202
for (engine=GpNextActive(0) ; engine ; engine=GpNextActive(engine))
203
value|= engine->Flush(engine);
206
return engine->Flush(engine);
209
Engine *GpNextEngine(Engine *engine)
211
return engine? engine->next : gistEngines;
214
Engine *GpNextActive(Engine *engine)
216
if (gistPreempt) return engine? 0 : gistPreempt;
217
else return engine? engine->nextActive : gistActive;
220
/* ------------------------------------------------------------------------ */
222
int GpSetTrans(const GpTransform *trans)
226
if (trans!=&gistT) gistT= *trans;
228
for (engine=GpNextActive(0) ; engine ; engine=GpNextActive(engine))
229
engine->ChangeMap(engine);
234
int GpLandscape(Engine *engine, int landscape)
237
for (engine=GpNextActive(0) ; engine ; engine=GpNextActive(engine))
238
engine->landscape= landscape;
240
engine->landscape= landscape;
245
void GpSetMap(const GpBox *src, const GpBox *dst, GpXYMap *map)
247
map->x.scale= (dst->xmax-dst->xmin)/(src->xmax-src->xmin);
248
map->x.offset= dst->xmin - map->x.scale*src->xmin;
249
map->y.scale= (dst->ymax-dst->ymin)/(src->ymax-src->ymin);
250
map->y.offset= dst->ymin - map->y.scale*src->ymin;
253
void GpDeviceMap(Engine *engine)
255
GpSetMap(&engine->transform.viewport, &engine->transform.window,
259
void GpComposeMap(Engine *engine)
261
GpMap *devx= &engine->devMap.x;
262
GpMap *devy= &engine->devMap.y;
263
GpMap *mapx= &engine->map.x;
264
GpMap *mapy= &engine->map.y;
266
devx->scale*(gistT.viewport.xmax-gistT.viewport.xmin)/
267
(gistT.window.xmax-gistT.window.xmin);
268
mapx->offset= devx->offset + devx->scale*gistT.viewport.xmin -
269
mapx->scale*gistT.window.xmin;
271
devy->scale*(gistT.viewport.ymax-gistT.viewport.ymin)/
272
(gistT.window.ymax-gistT.window.ymin);
273
mapy->offset= devy->offset + devy->scale*gistT.viewport.ymin -
274
mapy->scale*gistT.window.ymin;
277
/* ------------------------------------------------------------------------ */
279
/* Scratch space used by GpIntPoints and GpIntSegs */
280
static void *scratch= 0;
281
static long scratchPoints= 0, scratchSegs= 0;
283
static void MoreScratch(long np, long ns)
285
if (scratch) p_free(scratch);
288
scratch= (void *)p_malloc(sizeof(GpPoint)*np);
290
scratchSegs= (sizeof(GpPoint)*np)/sizeof(GpSegment);
293
scratch= (void *)p_malloc(sizeof(GpSegment)*ns);
295
scratchPoints= (sizeof(GpSegment)*ns)/sizeof(GpPoint);
299
long GpIntPoints(const GpXYMap *map, long maxPoints, long n,
300
const GpReal *x, const GpReal *y, GpPoint **result)
302
GpReal scalx= map->x.scale, offx= map->x.offset;
303
GpReal scaly= map->y.scale, offy= map->y.offset;
304
long i, np= maxPoints<n? maxPoints : n;
307
if (np+1>scratchPoints) MoreScratch(np+1, 0); /* allow for closure pt */
308
*result= point= scratch;
310
for (i=0 ; i<np ; i++) {
311
point[i].x= (short)(scalx*x[i]+offx);
312
point[i].y= (short)(scaly*y[i]+offy);
318
long GpIntSegs(const GpXYMap *map, long maxSegs, long n,
319
const GpReal *x1, const GpReal *y1,
320
const GpReal *x2, const GpReal *y2, GpSegment **result)
322
GpReal scalx= map->x.scale, offx= map->x.offset;
323
GpReal scaly= map->y.scale, offy= map->y.offset;
324
long i, ns= maxSegs<n? maxSegs : n;
327
if (ns>scratchSegs) MoreScratch(0, ns);
328
*result= seg= scratch;
330
for (i=0 ; i<ns ; i++) {
331
seg[i].x1= (short)(scalx*x1[i]+offx);
332
seg[i].y1= (short)(scaly*y1[i]+offy);
333
seg[i].x2= (short)(scalx*x2[i]+offx);
334
seg[i].y2= (short)(scaly*y2[i]+offy);
340
/* ------------------------------------------------------------------------ */
342
void GpPutGray(int nColors, GpColorCell *palette)
347
((int)palette->red+(int)palette->green+(int)palette->blue)/3;
353
void GpPutNTSC(int nColors, GpColorCell *palette)
358
(30*(int)palette->red+59*(int)palette->green+11*(int)palette->blue)/100;
364
void GpPutRGB(int nColors, GpColorCell *palette)
368
palette->red= palette->green= palette->blue= palette->gray;
374
int GpSetPalette(Engine *engine, GpColorCell *palette, int nColors)
376
if (!engine) return 0;
381
engine->palette= palette;
382
engine->nColors= nColors;
383
engine->colorChange= 1;
384
return engine->ChangePalette(engine);
387
int GpGetPalette(Engine *engine, GpColorCell **palette)
389
*palette= engine? engine->palette : 0;
390
return engine? engine->nColors : 0;
393
int GpDumpColors(Engine *engine, int colorMode)
396
for (engine=GpNextActive(0) ; engine ; engine=GpNextActive(engine))
397
{ engine->colorMode= colorMode; engine->colorChange= 1; }
399
engine->colorMode= colorMode; engine->colorChange= 1;
404
/* ------------------------------------------------------------------------ */
406
long GpClipCells(GpMap *map, GpReal *px, GpReal *qx,
407
GpReal xmin, GpReal xmax, long ncells, long *off)
411
GpReal scale= map->scale;
412
GpReal offset= map->offset;
414
xmin= xmin*scale+offset;
415
xmax= xmax*scale+offset;
416
if (xmin>xmax) {GpReal tmp=xmin; xmin=xmax; xmax=tmp;}
417
p= (*px)*scale+offset;
418
q= (*qx)*scale+offset;
420
if (p<q && q>=xmin && p<=xmax) {
421
dx= (q-p)/(GpReal)ncells;
423
imin= (long)((xmin-p)/dx);
429
imax= (long)((q-xmax)/dx);
440
if (p<xmin && q>xmax) {
441
if (q-xmax > xmin-p) { q-= xmin-p; p= xmin; }
442
else { p+= q-xmax; q= xmax; }
446
} else if (p>q && p>=xmin && q<=xmax) {
447
dx= (p-q)/(GpReal)ncells;
449
imax= (long)((xmin-q)/dx);
456
imin= (long)((p-xmax)/dx);
466
if (q<xmin && p>xmax) {
467
if (p-xmax > xmin-q) { p-= xmin-q; q= xmin; }
468
else { q+= p-xmax; p= xmax; }
484
/* ------------------------------------------------------------------------ */
486
int GpIntersect(const GpBox *box1, const GpBox *box2)
488
/* Algorithm assumes min<max for x and y in both boxes */
489
return box1->xmin<=box2->xmax && box1->xmax>=box2->xmin &&
490
box1->ymin<=box2->ymax && box1->ymax>=box2->ymin;
493
int GpContains(const GpBox *box1, const GpBox *box2)
495
/* Algorithm assumes min<max for x and y in both boxes */
496
return box1->xmin<=box2->xmin && box1->xmax>=box2->xmax &&
497
box1->ymin<=box2->ymin && box1->ymax>=box2->ymax;
500
void GpSwallow(GpBox *preditor, const GpBox *prey)
502
/* Algorithm assumes min<max for x and y in both boxes */
503
if (preditor->xmin>prey->xmin) preditor->xmin= prey->xmin;
504
if (preditor->xmax<prey->xmax) preditor->xmax= prey->xmax;
505
if (preditor->ymin>prey->ymin) preditor->ymin= prey->ymin;
506
if (preditor->ymax<prey->ymax) preditor->ymax= prey->ymax;
509
/* ------------------------------------------------------------------------ */
511
/* These recondite routines are required to handle editing a drawing
512
on one or more interactive engines. The restriction to few
513
routines builds in certain inefficiencies; if every drawing were always
514
associated with one interactive engine some of the inefficiency could
515
be reduced. These are not intended for external use. */
517
extern int gdNowRendering, gdMaxRendered;
518
int gdNowRendering= -1;
519
int gdMaxRendered= -1;
521
int GdBeginDr(Drauing *drawing, GpBox *damage, int landscape)
527
/* If drawing has incurred damage, report damage to ALL engines
528
interested in the drawing (not just active engines). */
529
for (eng=GpNextEngine(0) ; eng ; eng=GpNextEngine(eng))
530
if (eng->drawing==drawing) GpDamage(eng, drawing, damage);
533
/* Loop on active engines to alert them that drawing is coming. */
534
for (eng=GpNextActive(0) ; eng ; eng=GpNextActive(eng)) {
535
if (eng->drawing!=drawing) {
536
/* This engine is not marked as interested in this drawing.
537
Mark it, and reset damaged and lastDrawn flags so that no
538
elements will be inhibited. */
539
eng->drawing= drawing;
542
if (landscape != eng->landscape) {
543
eng->landscape= landscape;
544
/* This change will be detected and acted upon by the first call
545
to the ChangeMap method (GpSetTrans). */
547
/* The semantics here are subtle --
548
After a ClearDrawing, GdDetach zeroes eng->drawing in order to
549
communicate that the drawing has been cleared. Thus, the code
550
gets here on a GdDraw after the drawing has been cleared, so
551
the time has come to carry out the deferred clearing of this
552
engine's plotting surface. */
553
GpClear(eng, CONDITIONALLY);
556
} else if (eng->damaged) {
557
/* This engine was interested in the drawing, which has been
558
damaged. Clear damaged area in preparation for repair work.
559
(This is redundant if the damage was due to an X windows
560
expose event, but the resulting inefficiency is very small.) */
561
eng->ClearArea(eng, &eng->damage);
564
} else if (eng->lastDrawn<drawing->nElements-1) {
569
gdNowRendering= gdMaxRendered= -1;
573
int GdBeginSy(GpBox *tickOut, GpBox *tickIn, GpBox *viewport,
574
int number, int sysIndex)
580
/* Note that this is harmless if sysIndex>2*sizeof(long)--
581
just slightly inefficient in that ticks and elements will ALWAYS
582
be drawn... This shouldn't be a practical problem. */
583
if (sysIndex>sizeof(long)) {
584
sysMask= 1 << (sysIndex-sizeof(long));
587
sysMask= 1 << sysIndex;
591
/* Loop on active engines to determine whether any require ticks or
592
elements to be drawn. Set inhibit switches for ticks. */
593
for (eng=GpNextActive(0) ; eng ; eng=GpNextActive(eng)) {
594
if ( ! (eng->systemsSeen[sysIndex] & sysMask) ) {
595
/* this engine has never seen this system */
598
eng->systemsSeen[sysIndex]|= sysMask;
600
} else if (eng->damaged && GpIntersect(tickOut, &eng->damage)) {
601
/* engine damage touches this coordinate system--
602
redraw ticks if region between tickIn and tickOut damaged,
603
redraw elements if viewport damaged */
604
if (!tickIn || !GpContains(tickIn, &eng->damage)) {
607
} else eng->inhibit= 1;
608
if (number>eng->lastDrawn || GpIntersect(viewport, &eng->damage))
612
/* engine undamaged or damage doesn't touch this system--
613
redraw elements if any new ones, don't redraw ticks */
615
if (number>eng->lastDrawn) value|= 1;
622
int GdBeginEl(GpBox *box, int number)
627
/* Loop on active engines to determine whether any require this
628
element to be drawn, and to set inhibit switches so that some
629
may draw it and others not. */
630
for (eng=GpNextActive(0) ; eng ; eng=GpNextActive(eng)) {
631
if (number>eng->lastDrawn) {
632
/* this engine hasn't seen this element before */
635
if (eng->damaged && gdMaxRendered<=eng->lastDrawn) {
636
/* If this is the first new element, the damage flag
637
must be reset, and ChangeMap must be called to set the
638
clip rectangle back to its undamaged boundary. */
643
} else if (box && eng->damaged && GpIntersect(box, &eng->damage)) {
644
/* engine damage touches this element */
649
/* this element has been seen before and hasn't been damaged */
653
/* set number of element currently being drawn for GdEndDr */
654
gdNowRendering= number;
655
if (gdMaxRendered<gdNowRendering) gdMaxRendered= gdNowRendering;
664
/* Done with this drawing- reset inhibit, damaged, and lastDrawn flags */
665
for (eng=GpNextActive(0) ; eng ; eng=GpNextActive(eng)) {
666
if (eng->lastDrawn<gdMaxRendered) eng->lastDrawn= gdMaxRendered;
667
eng->inhibit= eng->damaged= 0;
671
void GpDamage(Engine *eng, Drauing *drawing, GpBox *box)
673
if (eng->drawing!=drawing || !eng->marked) return;
674
if (eng->ClearArea==&DefaultClearArea) {
675
/* This engine doesn't need to record the damage box */
677
} else if (eng->damaged) {
678
/* drawing is already damaged on this engine */
679
if (eng->damage.xmin>box->xmin) eng->damage.xmin= box->xmin;
680
if (eng->damage.xmax<box->xmax) eng->damage.xmax= box->xmax;
681
if (eng->damage.ymin>box->ymin) eng->damage.ymin= box->ymin;
682
if (eng->damage.ymax<box->ymax) eng->damage.ymax= box->ymax;
684
/* drawing is currently undamaged on this engine */
690
void GdDetach(Drauing *drawing, Engine *engine)
693
for (eng=GpNextEngine(0) ; eng ; eng=GpNextEngine(eng)) {
694
if (!drawing || eng->drawing==drawing) {
696
eng->inhibit= eng->damaged= 0;
702
/* ------------------------------------------------------------------------ */